C# 驅動版本 v1.6.x
本教程基於C#驅動 v1.6.x 。 Api 文檔見此處: http://api.mongodb.org/csharp/current/.
簡介
本教程介紹由10gen支持的,用於MongoDB的C#驅動。C# 驅動由兩個類庫組成:BSON Library和C# Driver。 BSON Library 可以獨立於 C# Driver 使用。 C# Driver 則必須需要 BSON Library。
你還可能對 C# 驅動序列化教程 感興趣。它是一個另外的教程因為它涵蓋了很多資料。
下載
C# 驅動既有源代碼也有二進制文件。BSON Library 和 C# Driver 都存在同一個知識庫里,而BSON Library可以獨立使用。
原文件可以從 github.com 進行下載。
我們使用 msysgit 作為我們的 Windows git 客戶端。可以到這里進行下載: http://msysgit.github.com/.
要復制知識庫的話,從git bash shell里運行以下命令:
$ cd <parentdirectory> $ git config --global core.autocrlf true $ git clone git://github.com/mongodb/mongo-csharp-driver.git $ cd mongo-csharp-driver $ git config core.autocrlf true
復制知識庫之前,必須將core.autocrlf的全局設置設為true。當復制完畢后,我們建議你將core.autocrlf的本地設置設為true(如上所示),這樣將來core.autocrlf的全局設置更改了也不會影響到這個知識庫。如果你到時候想把全局設置的core.autocrlf改為false,則運行:
$ git config --global core.autocrlf false
core.autocrlf設置的典型問題是git 報告整個文件都被修改了(由於行結尾的差異)。在知識庫創建后更改core.autocrlf的設置是相當沒勁的,所以在開始時就設好它是很重要的。
你可以通過點擊以下鏈接的Downloads按鈕來下載源文件的 zip 文件 (不用復制知識庫):
http://github.com/mongodb/mongo-csharp-driver
你可以在以下鏈接下載二進制文件(.msi 和 .zip 兩種格式) :
http://github.com/mongodb/mongo-csharp-driver/downloads
生成
目前我們使用 Visual Studio 2010 來生成C# 驅動。解決方案的名稱是 CSharpDriver-2010.sln.
依賴項
單元測試依賴 NUnit 2.5.9,它已包含在知識庫的依賴項文件夾中。你可以不用安裝NUnit就生成C#驅動,不過要運行單元測試則必須安裝NUnit(除非你用別的測試運行器)
運行單元測試
有三個工程包含單元測試:
1. BsonUnitTests
2. DriverUnitTests
3. DriverUnitTestsVB
BsonUnitTests 不連接 MongoDB 服務端。DriverUnitTests 和 DriverUnitTestsVB 連接一個運行在localhost上默認端口的MongoDB實例。
運行單元測試的一個簡單方法是將其中一個單元測試工程設為啟動項目並遵照以下說明配置工程(用 BsonUnitTests 做例子):
- 在”調試“頁簽里:
- 將”啟動操作“設為”啟動外部程序“
- 將外部程序設為: C:\Program Files (x86)\NUnit 2.5.9\bin\net-2.0\nunit.exe
- 將“命令行參數”設為: BsonUnitTests.csproj /config:Debug /run
- 將“工作目錄”設為: BsonUnitTest.csproj 所在的目錄
如果還想為單元測試運行在Release模式的話,為Release配置重復以上步驟 (使用 /config:Release 代替) 。
nunit.exe的實際路徑可能根據你的機器有輕微不同。
要運行 DriverUnitTests 和 DriverUnitTestsVB 執行相同的步驟 (如有必要適當修改).
安裝
如果你想安裝C#驅動到你的機器上,你可以用安裝程序 (見上面的下載說明)。安裝程序很簡單,只需把DLL復制到指定安裝目錄即可。
如果你下載了二進制zip文件,只需簡單地解壓文件並把它們放到任意地方。
注意:如果你下載的是.zip 文件,Windows 可能要你 "解除鎖定" 幫助文件。當你雙擊CSharpDriverDocs.chm文件時,如果 Windows 問你 "是否要打開此文件?" ,將“每次打開此文件時都詢問”旁的復選框勾掉,然后再點擊“打開”按鈕。或者還可以在 CSharpDriverDocs.chm 文件上右鍵,選擇“屬性”,然后在“常規”頁簽的頂部點擊“解除鎖定”按鈕。如果“解除鎖定”按鈕沒顯示的話,就沒必要解除鎖定了。
引用和命名空間
要使用 C# 驅動,需要添加以下DLL引用:
- MongoDB.Bson.dll
- MongoDB.Driver.dll
至少要在你的源文件里加上以下using語句:
using MongoDB.Bson; using MongoDB.Driver;
另外還可能經常用到以下using語句:
using MongoDB.Driver.Builders; using MongoDB.Driver.GridFS; using MongoDB.Driver.Linq;
在某些情況下如果你要使用C#驅動的某些可選部分的話,還可能用上以下某些using語句:
using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Conventions; using MongoDB.Bson.Serialization.IdGenerators; using MongoDB.Bson.Serialization.Options; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Wrappers;
BSON Library
C# 驅動在 BSON Library之上創建的。BSON Library處理BSON格式的所有細節,包括:I/O,序列化以及BSON文檔的內存中對象模型。
BSON對象模型的重要類有: BsonType, BsonValue, BsonElement, BsonDocument 和 BsonArray.
BsonType
這個枚舉用來指定BSON值得類型,定義為:
public enum BsonType { Double = 0x01, String = 0x02, Document = 0x03, Array = 0x04, Binary = 0x05, Undefined = 0x06, ObjectId = 0x07, Boolean = 0x08, DateTime = 0x09, Null = 0x0a, RegularExpression = 0x0b, JavaScript = 0x0d, Symbol = 0x0e, JavaScriptWithScope = 0x0f, Int32 = 0x10, Timestamp = 0x11, Int64 = 0x12, MinKey = 0xff, MaxKey = 0x7f }
BsonValue 及其子類
BsonValue 是一個抽象類,表示一個BSON類型的值。BsonType枚舉中定義的每一個值都對應了一個具體的BsonValue子類。要獲取一個BsonValue實例有幾種方法:
- 使用一個BsonValue的子類的公共構造函數 (如果有的話)
- 使用BsonValue的靜態 Create 方法
- 使用BsonValue的子類的靜態 Create 方法
- 使用BsonValue的子類的靜態屬性
- 使用隱式轉換到 BsonValue
使用靜態 Create 方法的好處是它們可以為經常使用的值返回預創建實例。它們還可以返回null(構造函數卻不能),當使用函數式構造創建BsonDocument處理可選元素時將很有用。靜態屬性引用經常使用的值的預創建實例。隱式轉換允許你使用原生的.NET值,不管BsonValue是否需要,而且.NET值會自動轉換為BsonValue。
BsonType 屬性
BsonValue 有一個名為 BsonType 的屬性,可以用來查詢BsonValue的實際類型。以下例子演示判斷BsonValue類型的幾種方法:
BsonValue value; if (value.BsonType == BsonType.Int32) { // 知道值是BsonInt32的實例 } if (value is BsonInt32) { // 另一種知道值是BsonInt32的方法 } if (value.IsInt32) { // 最簡單的知道值是BsonInt32的方法 }
As[Type] 屬性
BsonValue 有大量將BsonValue投射(cast)成它的子類或原生.NET類型的屬性。注意很重要的一點是,這些屬性都只是投射(cast),而不是轉換(conversion)。如果BsonValue不是對應的類型的話,將拋出一個 InvalidCastException 異常。 參見 To[Type] 方法,它進行的就是轉換操作,以及 Is[Type] 屬性,你可以在嘗試使用其中一個As[Type]屬性前用它查詢BsonValue的類型。
BsonDocument document; string name = document["name"].AsString; int age = document["age"].AsInt32; BsonDocument address = document["address"].AsBsonDocument; string zip = address["zip"].AsString;
Is[Type] 屬性
BsonValue 有以下布爾屬性,用來測試它是什么類型的BsonValue。這些可以如下使用:
BsonDocument document; int age = -1; if (document.Contains["age"] && document["age"].IsInt32) { age = document["age"].AsInt32; }
To[Type] 轉換方法
不像 As[Type] 方法,To[Type] 方法在可轉換類型間,如int和double,執行同樣的有限轉換。
ToBoolean 方法從不失敗。它使用 JavaScript 的似真非真定義: false, 0, 0.0, NaN, BsonNull, BsonUndefined 和 "" 都是false, 而其余的都是 true (包括字符串 "false").
ToBoolean 方法當處理的文檔可能對記錄true/false值時有不確定方法時就顯得特別有用:
if (employee["ismanager"].ToBoolean()) { // we know the employee is a manager // works with many ways of recording boolean values }
ToDouble, ToInt32, 和 ToInt64 方法當在數字類型間轉換時從不失敗,但是值如果不適合目標類型的話可能會被截斷。字符串可以轉換為數字類型,但如果字符串不能解析為目標類型的值的話將拋出異常。
靜態Create方法
由於 BsonValue 是一個抽象類,你不能創建BsonValue的實例(只能創建具體子類的實例)。 BsonValue 有一個靜態 Create 方法,參數只有一個,是object類型的,並在運行時確定要創建的BsonValue的實際類型。BsonValue的子類也有靜態的 Create 方法,由它們的需求而定制。
隱式轉換
隱式轉換是從以下.NET類型到BsonValue定義的:
- bool
- byte[]
- DateTime
- double
- Enum
- Guid
- int
- long
- ObjectId
- Regex
- string
這就不用再調用BsonValue構造函數或者Create方法了。例如:
BsonValue b = true; // b是一個 BsonBoolean 的實例 BsonValue d = 3.14159; // d 是一個 BsonDouble 的實例 BsonValue i = 1; // i 是一個 BsonInt32 的實例 BsonValue s = "Hello"; // s 是一個 BsonString 的實例
BsonMaxKey, BsonMinKey, BsonNull 和 BsonUndefined
這幾個類都是單例的,所以每個類只有一個實例存在。要引用這些實例,使用每個類的靜態 Value 屬性:
document["status"] = BsonNull.Value; document["priority"] = BsonMaxKey.Value;
注意 C# 的 null 和 BsonNull.Value 是兩個不同的東西。后者其實是一個 C# 對象,表示一個 BSON null 值 (這是很細微的區別,但在功能結構里卻扮演很重要的角色).
ObjectId 和 BsonObjectId
ObjectId 是一個結構體,維持一個BSON ObjectId的原始值。 BsonObjectId 是 BsonValue 的一個子類,其 Value 屬性是ObjectId類型。
這里是一些創建ObjectId值的常用方法:
var id1 = new ObjectId(); // 和 ObjectId.Empty 一樣 var id2 = ObjectId.Empty; // 全是0 var id3 = ObjectId.GenerateNewId(); // 生成新的唯一Id var id4 = ObjectId.Parse("4dad901291c2949e7a5b6aa8"); // 解析24位十六進制數字串
注意第一個例子C#與JavaScript有不同的表現。在C#里它創建了一個全是0的 ObjectId ,不過在 JavaScript 里它生成一個新的唯一Id。這個差異無法避免,因為在C#里值類型的構造函數總是把值初始化為全0。
BsonElement
BsonElement 是一個鍵值對,值是一個 BsonValue。它好比BsonDocument的積木,由0或者更多的元素組成。一般很少直接創建 BsonElements ,因為它們通常是不需要直接創建的。例如:
document.Add(new BsonElement("age", 21)); // 沒問題,但下一行更簡短 document.Add("age", 21); // 自動創建BsonElement
BsonDocument
BsonDocument 是鍵值對(BsonElement)的集合 。 它是BSON文檔的內存中對象模型。有三種方法創建並填充 BsonDocument:
- 創建一個新的文檔並調用Add和Set方法
- 創建一個新的文檔並連續調用Add和Set方法
- 創建一個新的文檔並使用C#的集合初始化語法(推薦)
BsonDocument 構造函數
BsonDocument 有以下構造函數:
- BsonDocument()
- BsonDocument(string name, BsonValue value)
- BsonDocument(BsonElement element)
- BsonDocument(Dictionary<string, object> dictionary)
- BsonDocument(Dictionary<string, object> dictionary, IEnumerable<string> keys)
- BsonDocument(IDictionary dictionary)
- BsonDocument(IDictionary dictionary, IEnumerable<string> keys)
- BsonDocument(IDictionary<string, object> dictionary)
- BsonDocument(IDictionary<string, object> dictionary, IEnumerable<string> keys)
- BsonDocument(IEnumerabe<BsonElement> elements)
- BsonDocument(params BsonElement[] elements)
- BsonDocument(bool allowDuplicateNames)
前兩個是最可能用到的。第一個創建一個空的文檔,第二個以一個元素創建文檔(這兩種情況當然都可以添加更多元素)。
所有構造函數(除了那個有allowDuplicateNames參數的之外)都簡單地調用了同參數的Add方法,所以參考相應的Add方法以了解新文檔是如何初始化填充的。
BsonDocument 一般不允許有重復的名稱,但如果你想有重復的名稱的話,就調用帶allowDuplicateNames參數的構造函數並傳入true。不建議你使用重復的名稱,此選項只是為了處理已有的可能存在重復名稱的BSON文檔。 MongoDB 不保證是否支持帶重復名稱的文檔,所以發送此類文檔到服務器是務必小心。
創建新文檔並調用Add和Set方法
這是傳統的用多行C#語句一步步創建並填充文檔的方法。例如:
BsonDocument book = new BsonDocument(); book.Add("author", "Ernest Hemingway"); book.Add("title", "For Whom the Bell Tolls");
創建新文檔並連續使用Add和Set方法
這個和前一個方法很類似,不過連續調用Add方法只需一行C#語句即可。例如:
BsonDocument book = new BsonDocument() .Add("author", "Ernest Hemingway") .Add("title", "For Whom the Bell Tolls");
創建新文檔並使用C#集合初始化語法(推薦使用)
這是在一行語句中創建和初始化BsonDocument的推薦方法。它使用C#集合初始化語法:
BsonDocument book = new BsonDocument { { "author", "Ernest Hemingway" }, { "title", "For Whom the Bell Tolls" } };
編譯器將這行代碼翻譯為與之匹配的Add方法:
BsonDocument book = new BsonDocument(); book.Add("author", "Ernest Hemingway"); book.Add("title", "For Whom the Bell Tolls");
常見的錯誤時忘了寫里面的花括弧。這會導致一個編譯錯誤。例如:
BsonDocument bad = new BsonDocument { "author", "Ernest Hemingway" };
會被編譯器翻譯成:
BsonDocument bad = new BsonDocument(); bad.Add("author"); bad.Add("Ernest Hemingway");
會導致編譯錯誤因為沒有Add方法是只接受一個string參數的。
創建嵌套的 BSON 文檔
嵌套 BSON 文檔通過設置元素的值為BSON文檔來進行創建。例如:
BsonDocument nested = new BsonDocument { { "name", "John Doe" }, { "address", new BsonDocument { { "street", "123 Main St." }, { "city", "Centerville" }, { "state", "PA" }, { "zip", 12345} }} };
這個創建了一個頂級文檔,有兩個元素 ("name" 和 "address")。 "address" 的值是一個嵌套的 BSON 文檔。
Add 方法
BsonDocument 有如下重載的Add方法
- Add(BsonElement element)
- Add(Dictionary<string, object> dictionary)
- Add(Dictionary<string, object> dictionary, IEnumerable<string> keys)
- Add(IDictionary dictionary)
- Add(IDictionary dictionary, IEnumerable<string> keys)
- Add(IDictionary<string, object> dictionary)
- Add(IDictionary<string, object> dictionary, IEnumerable<string> keys)
- Add(IEnumerable<BsonElement> elements)
- Add(string name, BsonValue value)
- Add(string name, BsonValue value, bool condition)
要注意的很重要的一點是有時候Add方法不會添加新元素。如果提供的值是null(或者最后一個重載中提供的condition是false)的話那元素就不會被添加。這在處理可選元素時就不用寫任何if語句或者條件表達式了。
例如:
BsonDocument document = new BsonDocument { { "name", name }, { "city", city }, // 如果city是null就不添加 { "dob", dob, dobAvailable } // 如果 dobAvailable是false則不添加 };
就比下面更簡潔和可讀性強:
BsonDocument document = new BsonDocument(); document.Add("name", name); if (city != null) { document.Add("city", city); } if (dobAvailable) { document.Add("dob", dob); }
如果你想在值缺失的情況下添加一個BsonNull,你可能會這么做。但更簡單的方法是使用C#的??運算符:
BsonDocument = new BsonDocument { { "city", city ?? BsonConstants.Null } };
IDictionary 重載從一個字典初始化一個 BsonDocument 。字典里的每一個鍵都變成元素的name,每一個值都映射為匹配的 BsonValue 並變成新元素的value。帶keys參數的重載讓你選擇加載哪個字典入口(還可能用keys參數來控制從字典里加載元素的順序)。
訪問 BsonDocument 元素
訪問 BsonDocument 元素的推薦做法是使用下面的索引:
- BsonValue this[int index]
- BsonValue this[string name]
- BsonValue this[string name, BsonValue defaultValue]
注意索引的返回值是 BsonValue,而不是 BsonElement。這其實讓 BsonDocuments 更容易使了 (如果你需要獲取實際的 BsonElements ,那就用 GetElement方法).
我們已經看過訪問 BsonDocument 元素的例子了。這里再來幾個:
BsonDocument book; string author = book["author"].AsString; DateTime publicationDate = book["publicationDate"].AsDateTime; int pages = book["pages", -1].AsInt32; // 默認值是 -1
BsonArray
這個類用來表示 BSON 數組。由於BSON文檔(元素用特殊命名慣例)恰巧對外表示為數組, BsonArray 類跟 BsonDocument 類是無關的,因為它倆用起來很不一樣。
構造函數
BsonArray 有以下構造函數:
- BsonArray()
- BsonArray(IEnumerable<bool> values)
- BsonArray(IEnumerable<BsonValue> values)
- BsonArray(IEnumerable<DateTime> values)
- BsonArray(IEnumerable<double> values)
- BsonArray(IEnumerable<int> values)
- BsonArray(IEnumerable<long> values)
- BsonArray(IEnumerable<ObjectId> values)
- BsonArray(IEnumerable<string> values)
- BsonArray(IEnumerable values)
所有帶參數的構造函數都調用匹配的Add方法。由於C#不提供自動從IEnumerable<T> 到 IEnumerable<object>的轉換,所以多重載是有必要的。
Add 和 AddRange 方法
BsonArray 具有以下的 Add 方法:
- BsonArray Add(BsonValue value)
- BsonArray AddRange(IEnumerable<bool> values)
- BsonArray AddRange(IEnumerable<BsonValue> values)
- BsonArray AddRange(IEnumerable<DateTime> values)
- BsonArray AddRange(IEnumerable<double> values)
- BsonArray AddRange(IEnumerable<int> values)
- BsonArray AddRange(IEnumerable<long> values)
- BsonArray AddRange(IEnumerable<ObjectId> values)
- BsonArray AddRange(IEnumerable<string> values)
- BsonArray AddRange(IEnumerable values)
注意Add方法只有一個參數。要創建並以多個值初始化一個 BsonArray ,請使用以下方法之一:
// 傳統方法 BsonArray a1 = new BsonArray(); a1.Add(1); a2.Add(2); // 連續調用 BsonArray a2 = new BsonArray().Add(1).Add(2); // values參數 int[] values = new int[] { 1, 2 }; BsonArray a3 = new BsonArray(values); // 集合初始化語法 BsonArray a4 = new BsonArray { 1, 2 };
索引
數組元素用整型索引進行訪問。比如 BsonDocument,元素的類型是 BsonValue。比如:
BsonArray array = new BsonArray { "Tom", 39 }; string name = array[0].AsString; int age = array[1].AsInt32;
C# 驅動
知道現在我們討論的都是 BSON 類庫。剩下的我們來說一下 C# 驅動。
線程安全
只有一小部分C#驅動是線程安全的。它們是: MongoClient, MongoServer, MongoDatabase, MongoCollection 和 MongoGridFS。通常用的比較多的類都不是線程安全的,包括 MongoCursor 和所有BSON類庫里的類 (除了 BsonSymbolTable 是線程安全的). 不特別標記為線程安全的都是線程非安全的。
所有類的所有靜態屬性和方法都是線程安全的。
MongoClient 類
這個類是操控MongoDB服務器的根對象。與服務器的連接是自動在后台處理的 (用了一個連接池來提升效率).
當連接到一個副本是,用的仍然只有一個MongoClient的實例,它代表一個完整的副本。驅動會自動找出所有副本里的成員並識別出當前的主服務器。
這個類的實例是線程安全的。
操作默認情況下,除非設置了,否則,所有的操作需要WriteConcern使用W = 1。換句話說,默認情況下,所有的寫操作都會阻塞,直到服務器已經確認。
連接字符串
連接MongoDB服務器的最簡單方法就是使用連接字符串。標准的連接字符串格式為:
mongodb://[username:password@]hostname[:port][/[database][?options]]
username 和 password 只有在MongoDB服務器使用了身份驗證時才出現。這些憑證信息將是所有數據庫的默認憑證。要驗證admin數據庫,在username里加上 "(admin)" 。如果要根據不同數據庫使用不同的憑證,在GetDatabase方法里傳入正確的憑證即可。
端口號是可選的,默認為 27017.
要連接多個服務器的話,用逗號分隔多個主機名(和端口號,如果有的話)。例如:
mongodb://server1,server2:27017,server2:27018
這個連接字段串指定了三個服務器 (其中兩個在同一台機器上,但端口號不一樣)。由於指定多個服務器會引起歧義,不知道到底是副本還是多個mongo(分片安裝中),服務器會進入一個連接的發現階段來確定它們的類型。這對於連接時間而言有點過頭了,不過可以通過在連接字符串里指定連接模式來避免:
mongodb://server1,server2:27017,server2:27018/?connect=replicaset
可選的模式有:自動 automatic (默認), 直接 direct, 副本replica set, 和 分片路由 shardrouter。連接模式的規則如下:
1. 如果連接模式指定為自動以外的,則使用之。
2. 如果在連接字符串里指定了副本名稱 (replicaset), 那么將使用副本模式。
3. 如果連接字符串里只列出了一個服務器,那么將使用直接模式。
4. 否則,將查找第一個響應的服務器,確定連接模式。
如果連接模式設為副本模式,驅動會去找主服務器,即使它不在字符串里列出,只要字符串里至少有一個服務器響應了(響應里將包含完整的副本和當前的主服務器名稱)。另外,其它服務器也會被找到並自動添加(或移除),甚至在初始化連接之后。這樣你就可以從副本里添加和移除服務器,驅動會自動處理這些變更。
正像上面所說的,連接字符串的可選部分是用來設置各種連接選項的。假設你想要直接連接到副本的一個成員不管它是不是當前主服務器(可能想監控它的狀態或者僅僅讀查詢)。你可以這么寫:
mongodb://server2/?connect=direct;readpreference=nearest
連接字符串的完整文檔可以看下面的連接:
http://www.mongodb.org/display/DOCS/Connections
和:
http://docs.mongodb.org/manual/applications/replication/#replica-set-read-preference
對SSL 的支持
驅動里已經支持了SSL。可以通過在連接字符串里加上 "ssl=true" 選項來進行配置。
mongodb://server2/?ssl=true
默認情況下,服務器證書會對本地受信任證書存儲進行驗證。這經常會在測試服務器沒有簽名證書的測試環境里引起一些問題。要緩和這個問題,可以添加另一個連接字符串選項 "sslverifycertificate=false" 來忽略任何證書錯誤。
身份認證
MongoDB 支持簡單直接的身份認證機制。你可以在 security and authentication docs page 了解更多。
C# 驅動有多種方法支持身份驗證。上面提到的連接字符串,可以指定默認憑證信息。通常在沒有提供其它憑證的時候都會用默認憑證。
有兩種方法來提供憑證。第一種,可以在運行時通過特定方法提供。這些憑證就會被用來執行想要的功能。第二種,也是更健壯的方法,是把憑證存儲在 MongoCredentialsStore 里。存在里面的 MongoCredentials 由數據庫作為鍵值,所以如果不同的數據庫需要不同的用戶,那么憑證存儲先去找第一個,如果沒找着,就退而求其次,看連接字符串里有沒有提供默認憑證,有則用之。
下面的例子使用了憑證存儲來定義"foo"數據的管理員憑證。使用“admin”或者“foo”以外的憑證去訪問數據將使用提供了默認憑證“test”的連接字符串。
var url = new MongoUrl("mongodb://test:user@localhost:27017"); var settings = MongoClientSettings.FromUrl(url); var adminCredentials = new MongoCredentials("admin", "user", true); settings.CredentialsStore.Add("admin", adminCredentials); var fooCredentials = new MongoCredentials("foo", "user", false); settings.CredentialsStore.Add("foo", fooCredentials); var client = new MongoClient(settings);
GetServer 方法
要從 MongoClient 的實例取得 MongoServer 的實例可以使用 GetServer 方法。
MongoServer 類
MongoServer 類是用來對驅動進行更多的控制。包含了獲取數據庫和通過簡單的socket發布一系列操作的高級方法,為的是保證一致性。
GetDatabase 方法
從 MongoServer 實例取得 MongoDatabase 實例(見下一節) 可以使用以下的 GetDatabase 方法或索引之一:
- MongoDatabase GetDatabase(MongoDatabaseSettings settings)
- MongoDatabase GetDatabase(string databaseName)
- MongoDatabase GetDatabase(string databaseName, MongoCredentials credentials)
- MongoDatabase GetDatabase(string databaseName, MongoCredentials credentials, WriteConcern writeConcern)
- MongoDatabase GetDatabase(string databaseName, WriteConcern writeConcern)
樣例代碼:
MongoClient client = new MongoClient(); // 連接到 localhost MongoServer server = client.GetServer(); MongoDatabase test = server.GetDatabase("test"); MongoCredentials credentials = new MongoCredentials("username", "password"); MongoDatabase salaries = server.GetDatabase("salaries", credentials);
大多數的數據設置都是從服務器對象繼承來的, GetDatabase 提供的重載可以對經常使用的設置進行覆蓋。要覆蓋其它設置,調用 CreateDatabaseSettings 並在調用 GetDatabase 之前更改任何你想要的設置,像這樣:
var databaseSettings = server.CreateDatabaseSettings("test"); databaseSettings.SlaveOk = true; var database = server.GetDatabase(databaseSettings);
GetDatabase 維系了一個 MongoDatabase 之前返回過的 實例表,因此如果以同樣的參數再次調用 GetDatabase 會再次得到相同的實例。
RequestStart/RequestDone 方法
有時候為了保證結果正確,需要在同一個連接里執行一系列操作。這比較少見,而且大多數時候沒有必要去調用 RequestStart/RequestDone。有必要這么做的一個例子是在w=0的WriteConcern的快速會話中調用了一系列的Insert,然后緊接着馬上查詢出這些數據來(在w=0的WriteConcern下,服務器里的寫操作會排隊,而且可能不會馬上對其它連接可見)。使用 RequestStart 可以在同一個連接里在寫的時候強制查詢,因此查詢直到服務器捕獲了寫操作之后才會執行。
通過使用RequestStart 和 RequestDone,線程可以從連接池里暫時保留一個連接,例如:
using(server.RequestStart(database)) {
// 在同一個連接里需要執行一系列操作
}
database 參數只是簡單地說明你要在這個請求期間要用哪些數據庫。這使服務器能夠對已經身份驗證通過的數據庫拿來就用 (如果沒用身份驗證那這個優化就沒關系了)。在這個請求期間你可以任意地使用其它數據庫了。
RequestStart (為這個線程)增加了一個計數,在完成后再減掉。保留的連接實際不是返回到連接池里,直到計數再次變為0。這說明嵌套調用 RequestStart 是沒有問題的。
其它的屬性和方法
參考其它的屬性和方法,請參閱api文檔。
MongoDatabase 類
這個類表示 MongoDB 服務器的數據庫。通常每個數據庫只有一個實例,除非你是用不同的設置來訪問同一個數據庫,這樣就是每個設置都有一個實例。
這個類的實例是線程安全的。
GetCollection 方法
此方法返回一個表示數據庫里集合的對象。當請求一個集合對象時,要同時制定集合的默認文檔類型。例如:
MongoDatabase hr = server.GetDatabase("hr"); MongoCollection<Employee> employees = hr.GetCollection<Employee>("employees");
集合並不限於只有一種文檔。默認的文檔類型在處理那種文檔時能更方便一點,但在需要時你完全可以指定另一種文檔。
大多數的集合設置是從數據庫繼承的,GetCollection 提供的重載可以對常用的設置進行覆蓋。要覆蓋其它的設置,調用 CreateCollectionSettings 並在調用 GetCollection 之前更改任何你想要的設置,像這樣:
var collectionSettings = database.CreateCollectionSettings<TDocument>("test"); collectionSettings.SlaveOk = true; var collection = database.GetCollection(collectionSettings);
GetCollection 維系了之前返回過的一個實例表,因此如果以同樣的參數再次調用 GetCollection 會得到同一個實例。
其它屬性和方法
參考其它的屬性和方法,請參閱api文檔。
MongoCollection<TDefaultDocument> 類
此類表示 MongoDB 數據庫里的集合。 <TDefaultDocument> 泛型參數指定了此集合默認文檔的類型。
此類的實例是線程安全的。
Insert<TDocument> 方法
要在集合里插入一個文檔,創建一個表示該文檔的對象並調用 Insert。對象可以是BsonDocument 的實例或者是可以成功序列化為BSON文檔的任何類的實例。例如:
MongoCollection<BsonDocument> books = database.GetCollection<BsonDocument>("books"); BsonDocument book = new BsonDocument { { "author", "Ernest Hemingway" }, { "title", "For Whom the Bell Tolls" } }; books.Insert(book);
如果有一個名為 Book 的類,代碼如下:
MongoCollection<Book> books = database.GetCollection<Book>("books"); Book book = new Book { Author = "Ernest Hemingway", Title = "For Whom the Bell Tolls" }; books.Insert(book);
InsertBatch 方法
使用InserBatch方法可以一次性插入多個文檔,例如:
MongoCollection<BsonDocument> books; BsonDocument[] batch = { new BsonDocument { { "author", "Kurt Vonnegut" }, { "title", "Cat's Cradle" } }, new BsonDocument { { "author", "Kurt Vonnegut" }, { "title", "Slaughterhouse-Five" } } }; books.InsertBatch(batch);
插入多個文檔時,使用 InsertBatch 比 Insert 效率更高。
FindOne 和 FindOneAs 方法
要從集合里獲取文檔,使用Find方法之一。FindOne是最簡單的一個。它返回找到的第一個文檔(當有多個文檔時你沒法確定是哪一個)。例如:
MongoCollection<Book> books; Book book = books.FindOne();
如果要讀取一個類型不是 <TDefaultDocument> 的文檔,就使用 FindOneAs 方法,可以覆蓋其返回文檔的類型,例如:
MongoCollection<Book> books; BsonDocument document = books.FindOneAs<BsonDocument>();
這里集合的默認文檔類型是 Book,但我們將其覆蓋了,指定結果為 BsonDocument 的實例。
Find 和 FindAs 方法
Find 和 FindAs 方法通過接受一個查詢,告訴服務器要返回那個文檔。 query 參數是 IMongoQuery 類型的。 IMongoQuery 接口標記了類可以用來進行查詢。構建查詢的最常用方法是要么使用Query建造類,要么自己創建一個QueryDocument (QueryDocument 是BsonDocument 的子類,同時實現了 IMongoQuery 因此可以用作查詢對象)。同時,通過使用 QueryWrapper 類,查詢可以是任何能序列化為BSON文檔的類型,不過這取決於你得保證序列化后的文檔表示的是一個有效的查詢對象。
其中一種查詢方法是自己創建 QueryDocument 對象:
MongoCollection<BsonDocument> books; var query = new QueryDocument("author", "Kurt Vonnegut"); foreach (BsonDocument book in books.Find(query)) { // do something with book }
另一種方法是使用 Query Builder (推薦):
MongoCollection<BsonDocument> books; var query = Query.EQ("author", "Kurt Vonnegut"); foreach (BsonDocument book in books.Find(query)) { // do something with book }
還有另一種查詢的方法是使用匿名類,不過這樣我們得把匿名對象進行封裝:
MongoCollection<BsonDocument> books; var query = Query.Wrap(new { author = "Kurt Vonnegut" }); foreach (BsonDocument book in books.Find(query)) { // do something with book }
如果想要讀取不是默認類型的文檔,則使用 FindAs 方法:
MongoCollection<BsonDocument> books; var query = Query<Book>.EQ(b => b.Author, "Kurt Vonnegut"); foreach (Book book in books.FindAs<Book>(query)) { // do something with book }
Save<TDocument> 方法
Save 方法是 Insert 和 Update的組合。如果文檔的 Id 有值,那么就假定這是一個已經存在的文檔,Save就會在文檔上調用Update(設置Upsert標記以防止它實際上是個新文檔)。否則就假定這是一個新文檔,Save會在首先將新生成的唯一值設到Id上,然后調用Insert。
例如,要修正一本書的書名錯誤:
MongoCollection<BsonDocument> books; var query = Query.And( Query.EQ("author", "Kurt Vonnegut"), Query.EQ("title", "Cats Craddle") ); BsonDocument book = books.FindOne(query); if (book != null) { book["title"] = "Cat's Cradle"; books.Save(book); }
調用Save方法的時候,TDocument 類必須要有Id。如果沒有的話可以調用Insert來插入文檔。
Update 方法
Update 方法用來更新已有文檔。Save方法的示例代碼還可以寫成:
MongoCollection<BsonDocument> books; var query = new QueryDocument { { "author", "Kurt Vonnegut" }, { "title", "Cats Craddle" } }; var update = new UpdateDocument { { "$set", new BsonDocument("title", "Cat's Cradle") } }; BsonDocument updatedBook = books.Update(query, update);
或者使用 Query 和 Update builders:
MongoCollection<BsonDocument> books; var query = Query.And( Query.EQ("author", "Kurt Vonnegut"), Query.EQ("title", "Cats Craddle") ); var update = Update.Set("title", "Cat's Cradle"); BsonDocument updatedBook = books.Update(query, update);
FindAndModify 方法
當你想要查找一個文檔並在一個原子操作里更新它時,就使用 FindAndModify。 FindAndModify 只更新一個文檔,配合使用具有排序標准的多文檔查詢來確定到底要更新哪個文檔。另外, FindAndModify 會返回符合條件的文檔 (不管是在更新前還是更新后) 而且可以指定要返回文檔的那些字段。
參考以下鏈接中的例子:
http://www.mongodb.org/display/DOCS/findAndModify+Command
對 FindAndModify 的調用如下:
var jobs = database.GetCollection("jobs"); var query = Query.And( Query.EQ("inprogress", false), Query.EQ("name", "Biz report") ); var sortBy = SortBy.Descending("priority"); var update = Update. .Set("inprogress", true) .Set("started", DateTime.UtcNow); var result = jobs.FindAndModify( query, sortBy, update, true // return new document ); var chosenJob = result.ModifiedDocument;
MapReduce 方法
Map/Reduce 是從集合里匯總數據的一種方法。集合里的每一個文檔(或者某些子集,如果可選查詢提供了的話)都被傳到map函數,該函數調用emit來產生中間值。然后中間值被傳到reduce函數進行匯總。
下面的例子摘選自Kristina Chodorow 和 Michael Dirolf寫的《MongoDB權威指南》第87頁。它計算了集合里的每一個鍵值被找到了多少次。
var map = "function() {" + " for (var key in this) {" + " emit(key, { count : 1 });" + " }" + "}"; var reduce = "function(key, emits) {" + " total = 0;" + " for (var i in emits) {" + " total += emits[i].count;" + " }" + " return { count : total };" + "}"; var mr = collection.MapReduce(map, reduce); foreach (var document in mr.GetResults()) { Console.WriteLine(document.ToJson()); }
其它屬性和方法
參考其它的屬性和方法,請參閱api文檔。
MongoCursor<TDocument> 類
Find 方法(以及它的各個變種) 不是馬上返回查詢的實際結果。而是返回一個能獲取到查詢結果的可遍歷的游標。查詢實際上並不是傳到服務器,直到嘗試獲取第一個結果(技術上而言,就是在由GetEnumerator返回的枚舉器第一次調用MoveNext時)。這說明了我們可以在獲取到結果之前以各種有趣的方式來控制查詢的結果。
MongoCursor 的實例不是線程安全的,至少在它們凍結(見下面)前是不安全的。一旦它們凍結了,它們就是線程安全的了,因為它們是只讀的(尤其是,GetEnumerator是線程安全的,所以同一個游標可以被多個線程使用)。
遍歷游標
要使用查詢結果最方便的方法就是用C#的foreach語句。例如:
var query = Query.EQ("author", "Ernest Hemingway"); var cursor = books.Find(query); foreach (var book in cursor) { // do something with book }
還可以用LINQ為IEnumerable<T>定義的擴展方法來遍歷游標:
var query = Query.EQ("author", "Ernest Hemingway"); var cursor = books.Find(query); var firstBook = cursor.FirstOrDefault(); var lastBook = cursor.LastOrDefault();
很重要的一點是游標將其引用的資源都釋放干凈了。要保證這一點的關鍵是確保調用了枚舉器的Dispose方法。foreach語句和LINQ擴展方法都保證了Dispose會被調用。除非你手動遍歷右邊,那就得自己負責調用 Dispose。
遍歷游標前修改它
游標有幾個屬性可以在遍歷控制返回結果前進行修改。有兩種修改游標的方法:
- 直接修改屬性
- 使用平滑接口來設置屬性
例如,如果想要取第101到110個結果,可以這樣寫:
var query = Query.EQ("status", "pending"); var cursor = tasks.Find(query); cursor.Skip = 100; cursor.Limit = 10; foreach (var task in cursor) { // do something with task }
或者使用平滑接口:
var query = Query.EQ("status", "pending"); foreach (var task in tasks.Find(query).SetSkip(100).SetLimit(10)) { // do something with task }
平滑接口在只設置少部分值時用着很爽。當設置比較多時可能用屬性方式更好一點。
一旦開始遍歷游標,它就變成“凍結”,你就不能再更改任何屬性了。所以要在遍歷前就設置好所有的屬性。
游標的可修改屬性
以下是游標的可修改屬性:
- BatchSize (SetBatchSize)
- Fields (SetFields)
- Flags (SetFlags)
- Limit (SetLimit)
- Options (SetOption and SetOptions)
- SerializationOptions (SetSerializationOptions)
- Skip (SetSkip)
- SlaveOk (SetSlaveOk)
括號里的方法名是對應的平滑接口方法。
平滑接口還支持額外的不常使用的選項,這些選項沒有對應的屬性:
- SetHint
- SetMax
- SetMaxScan
- SetMin
- SetShowDiskLoc
- SetSnapshot
- SetSortOrder
其它方法
MongoCursor 有一些方法用於某些特殊操作目的:
- Clone
- Count
- Explain
- Size
WriteConcern 類
WriteConcern 有好幾級,這個類就是用來表示這些級次的。 WriteConcern 只是應用在那些沒有返回值的操作 (所以它不應用在查詢和命令中)。它應用於這幾個 MongoCollection 方法: Insert, Remove, Save 和 Update.
WriteConcern 的要領是在 Insert, Remove, Save 或者 Update之后,緊接着調用GetLastError命令將消息發送到服務器,這樣驅動就可以操作成功了。另外,當使用副本時,有可能確認信息被復制到最少量的輔服務器上去。