原文链接 http://www.mongodb.org/display/DOCS/CSharp+Driver+Tutorial
本文主要介绍由 10gen 提供的C#驱动,该驱动主要由两个库组成:BSON Library 和C# Driver。其中 BSON Library 可单独使用,而C# Driver则需要BSON Library库的支持。
省略
省略
省略
省略
省略
C#驱动是BSON库的基础上开发的,该库主要用于处理BSON协议,包括I/O处理,序列化,内存中的BSON文档。主要的类有:BsonType, BsonValue, BsonElement, BsonDocument and BsonArray.
该枚举主要用来声明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是一个描述Bson 值的抽象类.每个BsonType枚举的值都一个BsonValue的子类与之对应。有如下几种方式来实例化一个BsonValue:
使用静态的Create方法的优点是对于经常使用的BosnValue该方法会预先创建好一个实例。它们同样也能返回一个null值(构造方法却不能)用于处理BsonDocuments中的可选字段.静态propertie用于经常使用的值。隐式转换实现了.net的类型和BsonValue的转换。
BsonValue有以下这些子类
BsonArray
BsonBinaryData
BsonBoolean
BsonDateTime
BsonDocument
BsonDouble
BsonInt32
BsonInt64
BsonJavaScript
BsonJavaScriptWithScope (a subclass of BsonJavaScript)
BsonMaxKey
BsonMinKey
BsonNull
BsonObjectId
BsonRegularExpression
BsonString
BsonSymbol
BsonTimestamp
BsonUndefined
BsonValue 有个BsonType 属性(property)你可以用它查询BsonValue的实际类型.下面的几个列子讲解如何获取一个BsonValue的实际类型
BsonValue value;
if (value.BsonType == BsonType.Int32) {
// we know value is an instance of BsonInt32
}
if (value is BsonInt32) {
// another way to tell that value is a BsonInt32
}
if (value.IsInt32) {
// the easiest way to tell that value is a BsonInt32
}
BsonValue有以下属性用于BsonValue值转换其子类或.Net 的主要类型。
AsBoolean (=> bool)
AsBsonArray
AsBsonBinaryData
AsBsonDateTime
AsBsonDocument
AsBsonJavaScript // also works if BsonType == JavaScriptWithScope
AsBsonJavaScriptWithScope
AsBsonMaxKey
AsBsonMinKey
AsBsonNull
AsBsonRegularExpression
AsBsonSymbol
AsBsonTimestamp
AsBsonUndefined
AsBsonValue
AsByteArray (=> byte[])
AsDateTime (=> DateTime)
AsDouble (=> double)
AsGuid (=> Guid)
AsInt32 (=> int)
AsInt64 (=> long)
AsNullableBoolean (=> bool?)
AsNullableDateTime (=> DateTime?)
AsNullableDouble (=> double?)
AsNullableGuid (=> Guid?)
AsNullableInt32 (=> int?)
AsNullableInt64 (=> long?)
AsNullableObjectId (=> ObjectId?)
AsObjectId (=> ObjectId)
AsRegex (=> Regex)
AsString (=> string)
如果类型无法转换将抛出InvalidCastException异常,例子代码
BsonDocument document;
string name = document["name"].AsString;
int age = document["age"].AsInt32;
BsonDocument address = document["address"].AsBsonDocument;
string zip = address["zip"].AsString;
BsonValue有下面这些properties用于测试其类型是否为该类型
IsBoolean
IsBsonArray
IsBsonBinaryData
IsBsonDateTime
IsBsonDocument
IsBsonJavaScript
IsBsonJavaScriptWithScope
IsBsonMaxKey
IsBsonMinKey
IsBsonNull
IsBsonRegularExpression
IsBsonSymbol
IsBsonTimestamp
IsBsonUndefined
IsDateTime
IsDouble
IsGuid
IsInt32
IsInt64
IsNumeric (true if type is Double, Int32 or Int64)
IsObjectId
IsString
使用样例
BsonDocument document;
int age = -1;
if (document.Contains["age"] && document["age"].IsInt32) {
age = document["age"].AsInt32;
}
下面这些方法可为.net基本类型和BsonValue提供转换
ToBoolean
ToDouble
ToInt32
ToInt64
这里的ToBoolean方法和javascript的true, false比较接近. false, 0, 0.0, NaN, BsonNull, BsonUndefined 和 "" 为FALSE, 其他(包括"false"字符串)都为TRUE.
调用ToDouble, ToInt32, ToInt64方法进行数字转换时永远不会失败。将字符串转换为相应的数字,如果解析字符串出错将会抛出一个异常。
因为BsonValue是抽象类所以你不能直接对其实例化。但是BsonValue有一个静态的create方法来进行实例化。
以下.net类型可以和BsonValue进行隐式转换
bool
byte[]
DateTime
double
Enum
Guid
int
long
ObjectId
Regex
string
通过隐式转换你可以不用调用构造方法或create方法来创建BsonValue实例。样例代码如下
BsonValue b = true; // b is an instance of BsonBoolean
BsonValue d = 3.14159; // d is an instance of BsonDouble
BsonValue i = 1; // i is an instance of BsonInt32
BsonValue s = "Hello"; // s is an instance of BsonString
这些类为单例模式,每个类只有一个实例。你可以使用它们的静态property来使用该类。
document["status"] = BsonNull.Value;
document["priority"] = BsonMaxKey.Value;
注意,C#的Null和BsonNull不是同一会事。
ObjectId是一个结构体列举BSON ObjectId的原始值。BsonObjectId是BsonValue的一个子类,它的property 是ObjectId类型。
创建一个ObjectId的方法如下
var id1 = new ObjectId(); // same as ObjectId.Empty
var id2 = ObjectId.Empty; // all zeroes
var id3 = ObjectId.GenerateNewId(); // generates new unique Id
var id4 = ObjectId.Parse("4dad901291c2949e7a5b6aa8"); // parses a 24 hex digit string
BsonElement是一个键值对,value就是BsonValue。该类主要用于生成包含一个或多个元素的 BsonDocument 块。你可以直接创建BsonElement,也可以在使用的时候间接的进行创建。
document.Add(new BsonElement("age", 21)); // OK, but next line is shorter
document.Add("age", 21); // creates BsonElement automatically
BsonDocument是一个键值对的集合。它是BSON document的内存对象。有三种方式来创建一个BSON document对象。
BsonDocument book = new BsonDocument();
book .Add("author", "Ernest Hemingway");
book .Add("title", "For Whom the Bell Tolls");
BsonDocument book = new BsonDocument()
.Add("author", "Ernest Hemingway")
.Add("title", "For Whom the Bell Tolls");
我们推荐使用该方法来创建和初始化BsonDocument。
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 nested = new BsonDocument {
{ "name", "John Doe" },
{ "address", new BsonDocument {
{ "street", "123 Main St." },
{ "city", "Centerville" },
{ "state", "PA" },
{ "zip", 12345}
}}
};
上面的代码创建了一个包含两个元素的文档(name 和 address),其中address元素是一个文档。
BsonDocument document = new BsonDocument {
{ "name", name },
{ "city", city }, // not added if city is null
{ "dob", dob, dobAvailable } // not added if dobAvailable is false
};
上面的代码要比下面的更加易读和简洁
BsonDocument document = new BsonDocument();
document.Add("name", name);
if (city != null) {
document.Add("city", city);
}
if (dobAvailable) {
document.Add("dob", dob);
}
BsonDocument = new BsonDocument {
{ "city", city ?? BsonConstants.Null }
};
我们推荐采用索引来访问BsonDocument的元素。
BsonDocument book;
string author = book["author"].AsString;
DateTime publicationDate = book["publicationDate"].AsDateTime;
int pages = book["pages", -1].AsInt32; // default value is -1
该类主要用于描述Bson 数组。
// traditional approach
BsonArray a1 = new BsonArray();
a1.Add(1);
a2.Add(2);
// fluent interface
BsonArray a2 = new BsonArray().Add(1).Add(2);
// values argument
int[] values = new int[] { 1, 2 };
BsonArray a3 = new BsonArray(values);
// collection initializer syntax
BsonArray a4 = new BsonArray { 1, 2 };
数组元素采用整数索引,索引的值为BsonValue。
BsonArray array = new BsonArray { "Tom", 39 };
string name = array[0].AsString;
int age = array[1].AsInt32;
上面的BSON库介绍完了,接下来我们将为大家介绍C#驱动。
只有少量的驱动类是线程安全的,MongoServer, MongoDatabase, MongoCollection 和 MongoGridFS 这些类是线程安全的,我通常使用的类如MongoCursor 以及BSON库中所有的类都是线程非安全的(BsonSymbolTable类排除在外,该类也是线程安全的)。除非文档明确指出否则该类都是非线程安全的。所有类的静态属性和方法都是线程安全的。
该类是操作数据库的根类。你需要连接的服务器都将创一个该类的实例。当你连接一个已存在的服务配置你将会得到同一个链接实例。该类的实例是线程安全的。
连接数据服务器最简单的方法使用连接字符串。标准格式如下
mongodb://[username:password@]hostname[:port][/[database][?options]]
如果你要连接一个需要认证的服务器username和password是必须有的。端口默认为27017。当需要连接多个服务器时需要用逗号隔开。写法如下
mongodb://server1,server2:27017,server2:27018
string connectionString = "mongodb://localhost";
MongoServer server = MongoServer.Create(connectionString);
你可以通过下面这些MongoServer的方法或者索引来实例化一个MongoDatabase。
MongoServer server = MongoServer.Create(); // connect to localhost
MongoDatabase test = server.GetDatabase("test");
MongoCredentials credentials = new MongoCredentials("username", "password");
MongoDatabase salaries = server.GetDatabase("salaries", credentials);
大部分数据库设置都继承服务器对象,通过GetDatabase你可以重写一些设置。要重写其他设置请在调用GetDatabase()方法之前调用CreateDatabaseSettings()方法。
var databaseSettings = server.CreateDatabaseSettings("test");
databaseSettings.SlaveOk = true;
var database = server[databaseSettings];
如果你使用相同的参数多次调用GetDatabase()方法将只返回同一个实例,不会创建新的实例。
有时候我们需要在同一个链接上进行一系列的操作确保能得到正确的结果。大多数情况下无需调用RequestStart/RequestDone方法。举个例子,当我们在SafeMode关闭的情况下要执行一系列insert操作并且要确保它们必须成功写入(当SafeMode关闭时,写操作需要进行排队并不是立即写入磁盘的)。使用RequestStart() 方法你可以强制使一个查询操作和写操作在同一个链接上,所以如果服务器没有提交写操作则查询操作不会被执行。
线程会对使用RequestStart/RequestDone的地方暂时保留连接对象。
server.RequestStart(database);
// a series of operations that must be performed on the same connection
server.RequestDone();
该类主要描述服务器上的数据库。通常情况下每个数据库只会有一个实例,除非你使用不同的设置访问同一个数据库。该类的实例是线程安全的。
该方法返回数据库中的一个集合。当我们获取一个集合时,同时会取到该集合相关的文档。
MongoDatabase hr = server.GetDatabase("hr");
MongoCollection<Employee> employees =
hr.GetCollection<Employee>("employees");
一个集合不会强制要求该集合中的文档类型统一,可以是多种不同类型的文档;但是你在获取文档时必须指明文档的类型。
大多数集合的设置都继承自数据库对象,GetCollection() 方法可以让你重写相关的设置。如果要重写其他设置请在调用GetCollection() 之前调用CreateCollectionSettings()进行修改。
var collectionSettings = database.CreateCollectionSettings<TDocument>("test");
collectionSettings.SlaveOk = true;
var collection = database.GetCollection(collectionSettings);
当你使用相同的参数调用GetCollection()获取一个集合时会得到相同的实例引用。
属性
该类主要用于操作一个数据库的集合,<TDefaultDocument>参数用于描述集合中文档的类型。该类的实例是线程安全的。
该方法用于向集合中插入一条文档记录,要插入的对象可以是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);
你可以使用该方法同时插入多条记录
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);
要从集合中检索文档使用Find方法。FindOne()方法是最简单的,它返回在集合中找到的第一条文档。
MongoCollection<Book> books;
Book book = books.FindOne();
如果你要读取的文档类型不是<TDefaultDocument>请用FindOneAs()方法,该方法允许重写返回文档的类型。
MongoCollection<Book> books;
BsonDocument document = books.FindOneAs<BsonDocument>();
MongoCollection<BsonDocument> books;
var query = new QueryDocument("author", "Kurt Vonnegut");
foreach (BsonDocument book in books.Find(query)) {
// do something with book
}
MongoCollection<BsonDocument> books;
var query = Query.EQ("author", "Kurt Vonnegut");
foreach (BsonDocument book in books.Find(query)) {
// do something with book
}
还有一种方式是使用匿名的query,但是该方式需要进行匿名转换
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.EQ("author", "Kurt Vonnegut");
foreach (Book book in books.FindAs<Book>(query)) {
// do something with book
}
Save方法是Insert和Update方法的组合。如果ID字段有值则假定该文档是存在的并保存所有更新,否则插入一条新的文档记录。
例如你要更改一本书的错误标题:
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);
}
该方法主要用于更新已存在的文档。
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);
使用该方法你可以查找并更新相应的文档在一个原子操作中进行。该方法总是更新一条文档,并且在更新后执行查询操作;当然你可以控制返回更新前的文档还是更新后的文档。
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;
Map/Reduce是一种从集合中聚合数据的方法。集合中的每个文档通过调用emit() 发送到map函数时会产生一个中间值。这个中间值再被发送到reduce函数进行聚合。
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());
}
属性:
Find()方法返回的结果是一个可被遍历的cursor。只有当我们去检索第一条结果时,查询才会被发送到服务器(确切的说应该是调用MoveNext()方法进行遍历时)。这意味着我们通过获取结果之前修改cursor来控制查询的结果。
MongoCursor的实例是非线程安全的,至少在被冻结之前(后面会有介绍)。一旦被冻结后将是线程安全的,因为此时它们是只读的。
最简单的查询结果的方式就是用C#的foreach
var query = Query.EQ("author", "Ernest Hemingway");
var cursor = books.Find(query);
foreach (var book in cursor) {
// do something with book
}
你也可以使用扩张的Linq来进行遍历.
var query = Query.EQ("author", "Ernest Hemingway");
var cursor = books.Find(query);
var firstBook = cursor.FirstOrDefault();
var lastBook = cursor.LastOrDefault();
一个Cursor在遍历前有很多属性可以修改以控制其返回的结果。有2种方式来修改。
例如我们想获取第100-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
}
SafeMode有很多级别,SafeMode主要用于没有返回值得操作,因此不适用于query和command。它主要用在MongoCollection的Insert, Remove, Save 和 Update方法。当一个Insert, Remove, Save 或 Update消息被发送到服务器后可以通过GetLastError()方法获取是否处理成功。
SafeMode类将不再变化。它的属性可设置并且可再次实例化。该类的实例在冻结之前是非线程安全的。