[轉載]MongoDB C# 驅動教程

本教程基於C#驅動 v1.6.x 。 Api 文檔見此處: http://api.mongodb.org/csharp/current/.java

簡介

本教程介紹由10gen支持的,用於MongoDB的C#驅動。C# 驅動由兩個類庫組成:BSON Library和C# Driver。 BSON Library 能夠獨立於 C# Driver 使用。 C# Driver 則必須須要 BSON Library。git

你還可能對 C# 驅動序列化教程 感興趣。它是一個另外的教程由於它涵蓋了不少資料。github

下載

C# 驅動既有源代碼也有二進制文件。BSON Library 和 C# Driver 都存在同一個知識庫裏,而BSON Library能夠獨立使用。web

原文件能夠從 github.com 進行下載。mongodb

咱們使用 msysgit 做爲咱們的 Windows git 客戶端。能夠到這裏進行下載: http://msysgit.github.com/.shell

要複製知識庫的話,從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,則運行:api

$ 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 作例子):

  • 在」調試「頁籤裏:
    1. 將」啓動操做「設爲」啓動外部程序「
    2. 將外部程序設爲: C:\Program Files (x86)\NUnit 2.5.9\bin\net-2.0\nunit.exe
    3. 將「命令行參數」設爲: BsonUnitTests.csproj /config:Debug /run
    4. 將「工做目錄」設爲:  BsonUnitTest.csproj 所在的目錄

若是還想爲單元測試運行在Release模式的話,爲Release配置重複以上步驟 (使用 /config:Release 代替) 。

nunit.exe的實際路徑可能根據你的機器有輕微不一樣。

要運行 DriverUnitTests 和 DriverUnitTestsVB 執行相同的步驟 (若有必要適當修改).

安裝

若是你想安裝C#驅動到你的機器上,你能夠用安裝程序 (見上面的下載說明)。安裝程序很簡單,只需把DLL複製到指定安裝目錄便可。

若是你下載了二進制zip文件,只需簡單地解壓文件並把它們放到任意地方。

注意:若是你下載的是.zip 文件,Windows 可能要你 "解除鎖定" 幫助文件。當你雙擊CSharpDriverDocs.chm文件時,若是 Windows 問你 "是否要打開此文件?" ,將「每次打開此文件時都詢問」旁的複選框勾掉,而後再點擊「打開」按鈕。或者還能夠在 CSharpDriverDocs.chm 文件上右鍵,選擇「屬性」,而後在「常規」頁籤的頂部點擊「解除鎖定」按鈕。若是「解除鎖定」按鈕沒顯示的話,就不必解除鎖定了。

引用和命名空間

要使用 C# 驅動,須要添加如下DLL引用:

  1. MongoDB.Bson.dll
  2. 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
}

ToDoubleToInt32, 和 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:

  1. 建立一個新的文檔並調用Add和Set方法
  2. 建立一個新的文檔並連續調用Add和Set方法
  3. 建立一個新的文檔並使用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 是沒有問題的。

RequestStart 返回了一個 IDisposable。若是是在using塊裏用 RequestStart ,爲了釋放鏈接最好儘快調用 RequestDone 。

其它的屬性和方法

參考其它的屬性和方法,請參閱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();
在上邊的例子,查詢實際上傳到服務器兩次(分別是 FirstOrDefault 和 LastOrDefault 調用的時候).

很重要的一點是遊標將其引用的資源都釋放乾淨了。要保證這一點的關鍵是確保調用了枚舉器的Dispose方法。foreach語句和LINQ擴展方法都保證了Dispose會被調用。除非你手動遍歷右邊,那就得本身負責調用 Dispose。

遍歷遊標前修改它

遊標有幾個屬性能夠在遍歷控制返回結果前進行修改。有兩種修改遊標的方法:

  1. 直接修改屬性
  2. 使用平滑接口來設置屬性

例如,若是想要取第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命令將消息發送到服務器,這樣驅動就能夠操做成功了。另外,當使用副本時,有可能確認信息被複制到最少許的輔服務器上去。

相關文章
相關標籤/搜索