[转载]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命令将消息发送到服务器,这样驱动就能够操做成功了。另外,当使用副本时,有可能确认信息被复制到最少许的辅服务器上去。

相关文章
相关标签/搜索