MongoDB實戰開發

來源:互聯網
上載者:User

標籤:

【目標】:本文將以實戰的形式,向您展示如何用C#訪問MongoDB,完成常見的資料庫操作任務, 同時,也將介紹MongoDB的用戶端(命令列工作模式)以及一些基礎的命令。

【說明】:MongoDB是什嗎?有什麼用?如果不清楚這些問題的,請自己google一下吧。

【適合對象】:完全沒有接觸MongoDB或對MongoDB有一點瞭解的C#開發人員。因此本文是一篇入門級的文章。

【樣本項目】:本文的完整樣本是一個簡單的【客戶,商品,訂單】業務情境, 預覽介面效果請點擊此處(但並不完全相同),也包含下載樣本項目的源碼。

讓我們開始MongoDB的實戰入門吧。

回到頂部下載MongoDB,並啟動它

您可以在這個地址下載到MongoDB: http://www.mongodb.org/downloads, 本文將以【mongodb-win32-i386-1.8.2-rc2】來示範MongoDB的使用。

下載好了嗎?我們繼續吧。請解壓縮您剛才下載的MongoDB的zip壓縮包,進入解包的bin目錄,會發現有一堆exe檔案。 現在,請開啟命令列視窗並切換到剛才的bin目錄,然後輸入以下命令:

這裡,我運行了程式mongod.exe,並告訴它資料檔案的儲存目錄(這個目錄要事先建立好),至於mongod的更多命令列參數,請輸入命令: mongod /? 來獲得。
如果您也看到了這個介面,那麼表示MongoDB的服務端已成功啟動了。

順便提一下:運行mongod後,它會顯示一些有用的資訊。比如:pid (進程ID), tcp port (監聽連接埠), http 連接埠(用於查看運行狀態), 作業系統版本,資料目錄,32 or 64位版本,如果是32位版本,它還會告訴你【資料有2G的限制】,所以正式使用建議運行64位版本。

接下來,我們還要去下載MongoDB的C#驅動,它可以讓我們在C#中使用MongoDB 。: https://github.com/samus/mongodb-csharp
我下載到的壓縮包是:samus-mongodb-csharp-0.90.0.1-93-g6397a0f.zip 。這個壓縮包本身也包含了一個Sample,有興趣的可以看看它。
我們在C#訪問MongoDB所需的驅動就是項目MongoDB了。編譯這個項目就能得到了,檔案名稱:MongoDB.dll

回到頂部在C#使用MongoDB

好了,有了前面的準備工作,我們可以開始在C#中使用MongoDB了。不過,由於本樣本項目的代碼也不少,因此本文將只展示與MongoDB互動的相關代碼, 更完整的代碼請自行查閱樣本項目。

接下來,本文示範通過C#完成【客戶資料】的一些基本的資料操作,還是先來定義一個客戶資料的類型吧。

public sealed class Customer{    [MongoId]    public string CustomerID { get; set; }    public string CustomerName { get; set; }    public string ContactName { get; set; }    public string Address { get; set; }    public string PostalCode { get; set; }    public string Tel { get; set; }}

說明:這就是一個簡單的類,而且代碼中的[MongoId]也是可以不要的(這個後面再說)。

在操作資料庫之前,我要說明一下:MongoDB在使用前,並不要求您事先建立好相應的資料庫,設計資料表結構!
在MongoDB中,沒有【表】的概念,取而代之的是【集合】,也沒有【資料記錄】的概念,取而代之的是【文檔】, 我們可以把【文檔】理解成一個【對象】,任意的對象,甚至可以有複雜的嵌套層次。
因此,我們不用再寫代碼從【資料表欄位】到C#類的【屬性,欄位】的轉換了,現在直接就可以讀寫整個對象了。
而且MongoDB不支援Join操作,所以,如果有【關聯】操作,就需要你自己來處理。

再來定義二個變數:

private static readonly string _connectionString = "Server=127.0.0.1";private static readonly string _dbName = "MyNorthwind";
新增記錄
public void Insert(Customer customer){    customer.CustomerID = Guid.NewGuid().ToString("N");    // 首先建立一個串連    using( Mongo mongo = new Mongo(_connectionString) ) {        // 開啟串連        mongo.Connect();        // 切換到指定的資料庫        var db = mongo.GetDatabase(_dbName);        // 根據類型擷取相應的集合        var collection = db.GetCollection<Customer>();        // 向集合中插入對象        collection.Insert(customer);    }}

上面的代碼中,每一行都有注釋,這裡就不再解釋了。

刪除記錄
public void Delete(string customerId){    using( Mongo mongo = new Mongo(_connectionString) ) {        mongo.Connect();        var db = mongo.GetDatabase(_dbName);        var collection = db.GetCollection<Customer>();        // 從集合中刪除指定的對象        collection.Remove(x => x.CustomerID == customerId);    }}
更新記錄
public void Update(Customer customer){    using( Mongo mongo = new Mongo(_connectionString) ) {        mongo.Connect();        var db = mongo.GetDatabase(_dbName);        var collection = db.GetCollection<Customer>();        // 更新對象        collection.Update(customer, (x => x.CustomerID == customer.CustomerID));    }}
擷取記錄列表
public List<Customer> GetList(string searchWord, PagingInfo pagingInfo){    using( Mongo mongo = new Mongo(_connectionString) ) {        mongo.Connect();        var db = mongo.GetDatabase(_dbName);        var collection = db.GetCollection<Customer>();        // 先建立一個查詢        var query = from customer in collection.Linq()                    select customer;        // 增加查詢過濾條件        if( string.IsNullOrEmpty(searchWord) == false )            query = query.Where(c => c.CustomerName.Contains(searchWord) || c.Address.Contains(searchWord));        // 先按名稱排序,再返回分頁結果.        return query.OrderBy(x => x.CustomerName).GetPagingList<Customer>(pagingInfo);    }}
擷取單個對象
public Customer GetById(string customerId){    using( Mongo mongo = new Mongo(_connectionString) ) {        mongo.Connect();        var db = mongo.GetDatabase(_dbName);        var collection = db.GetCollection<Customer>();        // 查詢單個對象        return collection.FindOne(x => x.CustomerID == customerId);    }}
回到頂部重構(簡化)代碼

從上面代碼可以看出,操作MongoDB大致都是這樣一個操作過程。

// 首先建立一個串連using( Mongo mongo = new Mongo(_connectionString) ) {    // 開啟串連    mongo.Connect();    // 切換到指定的資料庫    var db = mongo.GetDatabase(_dbName);    // 根據類型擷取相應的集合    var collection = db.GetCollection<Customer>();    // 【訪問collection,做你想做的操作】}

針對這個問題,我提供一個封裝類來簡化MongoDB的使用。

簡化後的CRUD代碼如下:

public void Insert(Customer customer){    customer.CustomerID = Guid.NewGuid().ToString("N");    using( MyMongoDb mm = new MyMongoDb() ) {        mm.GetCollection<Customer>().Insert(customer);    }}public void Delete(string customerId){    using( MyMongoDb mm = new MyMongoDb() ) {        mm.GetCollection<Customer>().Remove(x => x.CustomerID == customerId);    }}public void Update(Customer customer){    using( MyMongoDb mm = new MyMongoDb() ) {        mm.GetCollection<Customer>().Update(customer, (x => x.CustomerID == customer.CustomerID));    }}public Customer GetById(string customerId){    using( MyMongoDb mm = new MyMongoDb() ) {        return mm.GetCollection<Customer>().FindOne(x => x.CustomerID == customerId);    }}

看了上面這些代碼,您應該會覺得MongoDB的使用也很容易,對吧。
接下來,我來通過介面錄入一些資料,來看看我錄入的結果吧。

到這裡,你或許想知道:MongoDB有沒有一個自己的用戶端來查看資料呢?因為總不能只依賴自己寫代碼來查看資料吧?
是的,MongoDB也提供了一個用戶端來查看並維護資料庫。接下來,我們再來看看如何使用MongoDB的用戶端吧。

回到頂部使用MongoDB的用戶端查看資料

MongoDB內建一個Javascript shell,它可以從命令列與MongoDB執行個體互動。這個shell非常有用,通過它可以管理操作、檢查運行執行個體、查詢資料等操作。 
讓我們再回到命令列視窗模式下吧(沒辦法,MongoDB只提供這種介面),運行mongo.exe ,如

這就是MongoDB的用戶端的命令列模式了。通常我們在操作資料庫前,要切換【當前資料】,
MongoDB也是一樣,我們可以運行如下命令來查看資料庫列表,並切換資料庫,然後再查看集合列表,請看(我運行了三條命令)

注意:MongoDB區分名字的大小寫。

在MongoDB中,查看【當前資料庫】,可以使用命令【db】,
查看集合Customer的記錄總數:【db.Customer.count();】
查看 CustomerId = 1 的記錄:【db.Customer.findOne({"_id" : "1"});】,注意:查詢條件是一個文檔,這就是MongoDB的特色。
顯示了上面三條命令的執行結果:

嗯,怎麼有亂碼?CustomerId = 1 的記錄應該是這樣的才對呀?

看到這一幕,您應該不要懷疑是MongoDB的錯了,這個錯誤是由於MongoDB的用戶端使用的編碼是UTF-8, 而Windows 命令列程式 cmd.exe 使用的gb2312(我目前使用中文語言) 造成的。
解決辦法:
1. 執行MongoDB的【exit】命令退回到Windows命令列狀態(或者重新開啟命令列視窗),
2. 運行命令:【chcp 65001】,切換到UTF-8編碼下工作。
3. 設定命令列視窗的屬性,請參考:

再運行 mongo 進入mongo命令列,切換資料庫,並執行命令:【db.Customer.findOne({"_id" : "1"});】

現在可以看到漢字能正常顯示了,但最後卻顯示"Failed to write to logfile",對於這個問題,我們如果執行命令 【db.Customer.findOne({"_id" : "91"});】(id=91的記錄就是我最後錄入的,全是a的那條,前面上有), 可以發現沒有任何異常發生,因此認為這個問題還是和cmd.exe有關的。 如果切換回 chcp 936 ,這時將看到亂碼,但沒有"Failed to write to logfile",所以我將忽略這個錯誤。

回到頂部使用MongoDB的用戶端維護資料

下面我來示範一下如何使用MongoDB的用戶端來執行一些基本的資料維護功能。還是從CRUD操作開始吧,請看,為了方便,我將在一個中執行多個命令。
注意:MongoDB的文檔使用的是一種稱為BSON格式的對象,與Javascript中的JSON類似。

在上面的樣本中,每個命令後,我加了一個紅圈。在樣本中,我先切換到 MyTest 資料庫(它並不存在,但沒關係), 然後我定義了一個文檔 item 並插入到集合 table1 中,然後又定義了一個文檔 item2,也插入到集合 table1 中。 注意:item , item2 的結構完全不同,但能放在一個集合中(不建議這樣做)。最後調用 find() 顯示集合中的所有文檔。
此時,您有沒有注意到:【每個文檔有一個名為 "_id" 的成員】,我可沒有定義啊。
其實,MongoDB會為每個文檔都建立這樣一個文檔成員,我們指定的 "key", "id" 對於MongoDB來說: 它們並不是【文檔的主鍵】,MongoDB只認 "_id",你可以指定,但如果不指定,MongoDB就自動添加。

此時,你可以看看前二張圖片,可以發現:在定義Customer類時,有一個成員CustomerID此時卻不存在! 我們可以再看一下Customer的定義:

public sealed class Customer{    [MongoId]    public string CustomerID { get; set; }    public string CustomerName { get; set; }    public string ContactName { get; set; }    public string Address { get; set; }    public string PostalCode { get; set; }    public string Tel { get; set; }}

此時,您應該發現CustomerID這個成員有一個[MongoId]的特性。正是由於這個特性,驅動程式將把CustomerID映射為"_id"來使用。

好了,再次回到命令列,我要示範其它的命令。請看:

為了要更新某個文檔,我們要使用findOne()方法找到要修改的文檔對象,並將它儲存一個變數t中,然後,修改它的屬性, 接著調用update()方法就可以更新文檔了,注意在調用update()時,第一個參數【修改範圍】是採用文檔的形式給出的, 第二參數才是要更新的新對象。在刪除時,刪除條件也是採用文檔的形式指定的。處處使用文檔,這就是MongoDB的特色。

前面的範例程式碼中,我使用了find()和findOne(),它們是有區別的:findOne()只返回一個文檔對象,find()返回一個集合列表, 如果不指定過濾範圍,它將返回整個集合,但在用戶端中最多隻顯示前20個文檔。

再來個複雜的查詢:搜尋日期範圍是 2011-06-25 到 2011-06-26 之間的訂單記錄。由於返回的結果太長,我的將不顯示它們。
注意:MongoDB的查詢條件中,並沒有 >, <, >= , <= 這些運算子,而是使用 "$lt", "$lte", "$gt", "$gte" 這種方式作為文檔的KEY來使用的, 因此一個簡單的 OrderDate >= "2006-06-25" and OrderDate < "2006-06-26" 要寫成如下方式:

如果遇到 or 就更麻煩了,如:CustomerId = 1 or CustomerId = 2 ,有二種寫法:

文法不難,相信能看懂JSON的人,也能看懂這二條命令。

再來個分頁的命令:

與LINQ的文法類似,好理解。

MongoDB用戶端還支援其它的文法,這裡就不一一介紹了。因為我們的目標是在C#中使用MongoDB,在MongoDB提供的C#驅動中, 我們並不需要寫那樣麻煩的查詢條件,只需要按LINQ的文法寫查詢就可以了,因此會很容易使用。 不過,有些維護性的操作,我們只能通過命令的方式去執行,比如:刪除集合,刪除資料庫。

執行【db.runCommand({"drop" : "table1"});】便可以刪除一個集合,也可以執行命令【db.table1.drop();】,二者的效果是一樣的。
再來看看如何刪除資料庫的命令:

注意:命令【db.runCommand({"dropDatabase": 1});】只能刪除【當前資料庫】,所以要先切換當前資料庫, 然後執行這個命令,執行刪除資料庫的命令後,我們再用命令【show dbs;】,探索資料庫【MyTest】已不存在,即刪除成功。 刪除資料庫還有一個方法:還記得我前面啟動mongod.exe時給它傳遞了一個參數 【-dbpath "H:\AllTempFiles\mongodb\data"】嗎? 我們現在去那個目錄看一下有什麼東西。

看了這張圖,您有沒有想過:這二個以【MyNorthwind】開頭的檔案會不會就是資料庫的檔案呢? 我現在就刪除看看,先停止mongod.exe,然後刪除檔案(由於我目前只有一個資料庫,我把目錄下的檔案全刪除了),刪除後:

現在,我再來啟動mongod.exe,然後在用戶端執行命令【show dbs;】看看:

現在可以發現我們之前的【MyNorthwind】資料庫沒有了,當然也就是刪除了。 說明一下:現在這二個資料庫,是MongoDB內建的,用於特殊用途的,我們可以不理會它們。

好了,我們還是再來看看MongoDB提供的C#驅動提供了什麼東西吧。

回到頂部MongoDB提供的C#驅動

我把MongoDB提供的C#驅動中認為比較重要的類做了個:

再來看看我前面給出一段操作MongoDB的代碼:

// 首先建立一個串連using( Mongo mongo = new Mongo(_connectionString) ) {    // 開啟串連    mongo.Connect();    // 切換到指定的資料庫    var db = mongo.GetDatabase(_dbName);    // 根據類型擷取相應的集合    var collection = db.GetCollection<Customer>();    // 【訪問collection,做你想做的操作】}

這段代碼大致也說明了在C#中操作MongoDB的一個過程,主要涉及中的前三個類,這三個類也是最核心的類。 這裡值得一提的是:LinqExtensions.Linq()方法可以讓我們在寫查詢時, 方便地使用LINQ的優雅文法,而不是一堆複雜的文檔條件!這也是我選擇這個驅動的原因。

還記得我前面舉過幾個在命令列中調用runCommand的樣本嗎?如果在C#中也需要執行這樣的操作,可以調用MongoDatabase.SendCommand() 方法。比如:刪除集合Category,我們可以寫成:

void DeleteCategoryCollection(){    using( MyMongoDb mm = new MyMongoDb() ) {        mm.CurrentDb.SendCommand(new Document("drop", "Category"));    }}

【MongoIdAttribute】:可以讓我們將一個C#類的資料成員映射到文檔的"_id"屬性。前面有樣本說明,這裡就不再多說了。

【MongoAliasAttribute】:可以讓我們將一個C#類的資料成員在映射到文檔時採用其它的屬性名稱。
比如:我希望將CustomerName成員在儲存到MongoDB時,採用CName來儲存。

[MongoAlias("CName")]public string CustomerName { get; set; }

【MongoIgnoreAttribute】:可以讓一個C#類在儲存到MongoDB時,忽略某些成員。請看下面的代碼:

public sealed class OrderItem : MyDataItem{    [MongoId]        // 這個成員將映射到 "_id"    public string OrderID { get; set; }    [ScriptIgnore]    // 在JSON序列化時,忽略這個成員    public DateTime OrderDate { get; set; }    [MongoIgnore]    // 在儲存到MongoDB時,忽略這個成員    public string CustomerName { get; set; }    // .... 還有其它的屬性。    // 加這個屬性僅僅為了在用戶端中能更容易的顯示,要不然,用戶端處理格式轉換實在是麻煩。    // 它將不會被寫入到資料庫。    [MongoIgnore]    public string OrderDateText { get { return this.OrderDate.ToString("yyyy-MM-dd HH:mm:ss"); } }}
回到頂部MongoDB不支援在查詢資料庫時使用Join操作

在MongoDB中,一個文檔就是一個完整的對象,所以擷取一個對象時,並不需要關聯式資料庫的那種JOIN文法。 在上面定義的OrderItem中,CustomerName並沒有儲存到資料庫,而是在載入時,採用了【引用】的設計方式, 根據CustomerID去訪問集合Customer來擷取對應的CustomerName ,這也算是JOIN的常見使用情境了。

樣本項目中有一個需求:根據一個日期範圍查詢訂單列表(支援分頁)。我是這樣實現這個查詢操作的。

/// <summary>/// 根據指定的查詢日期範圍及分頁參數,擷取訂單記錄列表/// </summary>/// <param name="dateRange">日期範圍</param>/// <param name="pagingInfo">分頁參數</param>/// <returns>訂單記錄列表</returns>public List<OrderItem> Search(QueryDateRange dateRange, PagingInfo pagingInfo){    dateRange.EndDate = dateRange.EndDate.AddDays(1);    using( MyMongoDb mm = new MyMongoDb() ) {        var collection = mm.GetCollection<OrderItem>(STR_Orders);        var query = from ord in collection.Linq()                    where ord.OrderDate >= dateRange.StartDate && ord.OrderDate < dateRange.EndDate                    orderby ord.OrderDate descending                    select new OrderItem {                        OrderID = ord.OrderID,                        CustomerID = ord.CustomerID,                        OrderDate = ord.OrderDate,                        SumMoney = ord.SumMoney,                        Finished = ord.Finished                    };        // 擷取訂單列表,此時將返回符合分頁的結果。        List<OrderItem> list = query.GetPagingList<OrderItem>(pagingInfo);        // 擷取訂單列表中所有的客戶ID        string[] cids = (from ord in list                          where string.IsNullOrEmpty(ord.CustomerID) == false                         select ord.CustomerID)                         .Distinct()                         .ToArray();        // 找到所有客戶記錄        Dictionary<string, Customer> customers =                            (from c in mm.GetCollection<Customer>().Linq()                             where cids.Contains(c.CustomerID)                             select new Customer {                                 CustomerID = c.CustomerID,                                 CustomerName = c.CustomerName                             })                            .ToDictionary(x => x.CustomerID);        // 為訂單列表結果設定CustomerName        foreach( OrderItem ord in list ) {            Customer c = null;            if( string.IsNullOrEmpty(ord.CustomerID) == false && customers.TryGetValue(ord.CustomerID, out c) )                ord.CustomerName = c.CustomerName;            else                 ord.CustomerName = string.Empty;        }        return list;    }}
回到頂部擷取MongoDB服務端狀態

我們再來看一下當時啟動服務端的截屏吧:

注意最後一行,它告訴我們它有一個WEB介面,連接埠是 28017 ,現在我就去看看那是個什麼樣子的。

可以看到它提供了一些服務端的狀態資訊。 我們還可以通過訪問【http://localhost:28017/_status】來獲得以JSON方式的統計資訊。

我們還可以通過運行用戶端的命令【db.runCommand({"serverStatus" : 1});】來擷取這些資訊:

 

好了,就說到這裡吧。接下來,您也可以寫點代碼嘗試一下,或者下載我準備的樣本項目參考一下。

點擊此處下載範例程式碼

 

MongoDB實戰開發

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.