发布于 2015-09-14 15:02:35 | 143 次阅读 | 评论: 0 | 来源: 网络整理
This tutorial is for v1.6.x of the C# Driver. Api documentation can be found here: http://api.mongodb.org/csharp/current.
This tutorial introduces the 10gen supported C# Driver for MongoDB. The C# Driver consists of two libraries: the BSON Library and the C# Driver. The BSON Library can be used independently of the C# Driver if desired. The C# Driver requires the BSON Library.
You may also be interested in the C# Driver Serialization Tutorial. It is a separate tutorial because it covers quite a lot of material.
The C# Driver is available in source and binary form. While the BSON Library can be used independently of the C# Driver they are both stored in the same repository.
The source may be downloaded from github.com.
We use msysgit as our Windows git client. It can be downloaded from: http://msysgit.github.com.
To clone the repository run the following commands from a 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
You must set the global setting for core.autocrlf to true before cloning the repository. After you clone the repository, we recommend you set the local setting for core.autocrlf to true (as shown above) so that future changes to the global setting for core.autocrlf do not affect this repository. If you then want to change your global setting for core.autocrlf to false run:
git config --global core.autocrlf false
The typical symptom of problems with the setting for core.autocrlf is git reporting that an entire file has been modified (because of differences in the line endings). It is rather tedious to change the setting of core.autocrlf for a repository after it has been created, so it is important to get it right from the start.
We are currently building the C# Driver with Visual Studio 2010. The name of the solution file is CSharpDriver-2010.sln.
The unit tests depend on NUnit 2.5.9, which is included in the dependencies folder of the repository. You can build the C# Driver without installing NUnit, but you must install NUnit before running the unit tests (unless you use a different test runner).
There are three projects containing unit tests:
The BsonUnitTests do not connect to a MongoDB server. The DriverUnitTests and DriverUnitTestsVB connect to an instance of MongoDB running on the default port on localhost.
An easy way to run the unit tests is to set one of the unit test projects as the startup project and configure the project settings as follows (using BsonUnitTests as an example):
Repeat the above steps for the Release configuration (using /config:Release instead) if you also want to run unit tests for Release builds.
The exact location of the nunit.exe program might vary slightly on your machine.
To run the DriverUnitTests and DriverUnitTestsVB perform the same steps (modified as necessary).
If you want to install the C# Driver on your machine you can use the setup program (see above for download instructions). The setup program is very simple and just copies the DLLs to your specified installation directory.
If you downloaded the binaries zip file simply extract the files and place them wherever you want them to be.
注解
If you download the .zip file Windows might require you to “Unblock” the help file. If Windows asks “Do you want to open this file?” when you double click on the CSharpDriverDocs.chm file, clear the check box next to “Always ask before opening this file” before pressing the Open button. Alternatively, you can right click on the CSharpDriverDocs.chm file and select Properties, and then press the Unblock button at the bottom of the General tab. If the Unblock button is not present then the help file does not need to be unblocked.
To use the C# Driver you must add references to the following DLLs:
As a minimum add the following using statements to your source files:
using MongoDB.Bson;
using MongoDB.Driver;
Additionally you will frequently add some of the following using statements:
using MongoDB.Driver.Builders;
using MongoDB.Driver.GridFS;
using MongoDB.Driver.Linq;
In some cases you might add some of the following using statements if you are using some of the optional parts of the C# Driver:
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;
The C# Driver is built on top of the BSON Library, which handles all the details of the BSON specification, including: I/O, serialization, and an in-memory object model of BSON documents.
The important classes of the BSON object model are: BsonType, BsonValue, BsonElement, BsonDocument and BsonArray.
This enumeration is used to specify the type of a BSON value. It is defined as:
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 is an abstract class that represents a typed BSON value. There is a concrete subclass of BsonValue for each of the values defined by the BsonType enum. There are several ways to obtain an instance of BsonValue:
The advantage of using the static Create methods is that they can return a pre-created instance for frequently used values. They can also return null (which a constructor cannot) which is useful for handling optional elements when creating BsonDocuments using functional construction. The static properties refer to pre-created instances of frequently used values. Implicit conversions allow you to use primitive .NET values wherever a BsonValue is expected, and the .NET value will automatically be converted to a BsonValue.
BsonValue has a property called BsonType that you can use to query the actual type of a BsonValue. The following example shows several ways to determine the type of a 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 has a number of properties that cast a BsonValue to one of its subclasses or a primitive .NET type. It is important to note that these all are casts, not conversions. They will throw an InvalidCastException if the BsonValue is not of the corresponding type. See also the To[Type] methods which do conversions, and the Is[Type] properties which you can use to query the type of a BsonValue before attempting to use one of the As[Type] properties.
BsonDocument document;
string name = document["name"].AsString;
int age = document["age"].AsInt32;
BsonDocument address = document["address"].AsBsonDocument;
string zip = address["zip"].AsString;
BsonValue has the following boolean properties you can use to test what kind of BsonValue it is. These can be used as follows:
BsonDocument document;
int age = -1;
if (document.Contains["age"] && document["age"].IsInt32) {
age = document["age"].AsInt32;
}
Unlike the As[Type] methods, the To[Type] methods perform some limited conversion between convertible types, like int and double.
The ToBoolean method never fails. It uses JavaScript’s definition of truthiness: false, 0, 0.0, NaN, BsonNull, BsonUndefined and "" are false, and everything else is true (include the string "false").
The ToBoolean method is particularly useful when the documents you are processing might have inconsistent ways of recording true/false values:
if (employee["ismanager"].ToBoolean()) {
// we know the employee is a manager
// works with many ways of recording boolean values
}
The ToDouble, ToInt32, and ToInt64 methods never fail when converting between numeric types, though the value might be truncated if it doesn’t fit in the target type. A string can be converted to a numeric type, but an exception will be thrown if the string cannot be parsed as a value of the target type.
Because BsonValue is an abstract class you cannot create instances of BsonValue (only instances of concrete subclasses). BsonValue has a static Create method that takes an argument of type object and determines at runtime the actual type of BsonValue to create. Subclasses of BsonValue also have static Create methods tailored to their own needs.
Implicit conversions are defined from the following .NET types to BsonValue:
These eliminate the need for almost all calls to BsonValue constructors or Create methods. For example:
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
These classes are singletons, so only a single instance of each class exists. You refer to these instances using the static Value property of each class:
document["status"] = BsonNull.Value;
document["priority"] = BsonMaxKey.Value;
Note that C# null and BsonNull.Value are two different things. The latter is an actual C# object that represents a BSON null value (it’s a subtle difference, but plays an important role in functional construction).
ObjectId is a struct that holds the raw value of a BSON ObjectId. BsonObjectId is a subclass of BsonValue whose Value property is of type ObjectId.
Here are some common ways of creating ObjectId values:
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
Note that the first example behaves differently in C# than in JavaScript. In C# it creates an ObjectId of all zeroes, but in JavaScript it generates a new unique Id. This difference can’t be avoided because in C# the default constructor of a value type always initializes the value to all zeros.
A BsonElement is a name/value pair, where the value is a BsonValue. It is used as the building block of BsonDocument, which consists of zero or more elements. You will rarely create BsonElements directly, as they are usually created indirectly as needed. For example:
document.Add(new BsonElement("age", 21)); // OK, but next line is shorter
document.Add("age", 21); // creates BsonElement automatically
A BsonDocument is a collection of name/value pairs (represented by BsonElements). It is an in-memory object model of a BSON document. There are three ways to create and populate a BsonDocument:
BsonDocument has the following constructors:
The first two are the ones you are most likely to use. The first creates an empty document, and the second creates a document with one element (in both cases you can of course add more elements).
All the constructors (except the one with allowDuplicateNames) simply call the Add method that takes the same parameters, so refer to the corresponding Add method for details about how the new document is initially populated.
A BsonDocument normally does not allow duplicate names, but if you must allow duplicate names call the constructor with the allowDuplicateNames parameter and pass in true. It is not recommended that you allow duplicate names, and this option exists only to allow handling existing BSON documents that might have duplicate names. MongoDB makes no particular guarantees about whether it supports documents with duplicate names, so be cautious about sending any such documents you construct to the server.
This is a traditional step by step method to create and populate a document using multiple C# statements. For example:
BsonDocument book = new BsonDocument();
book.Add("author", "Ernest Hemingway");
book.Add("title", "For Whom the Bell Tolls");
This is similar to the previous approach but the fluent interface allows you to chain the various calls to Add so that they are all a single C# statement. For example:
BsonDocument book = new BsonDocument()
.Add("author", "Ernest Hemingway")
.Add("title", "For Whom the Bell Tolls");
This is the recommended way to create and initialize a BsonDocument in one statement. It uses C#’s collection initializer syntax:
BsonDocument book = new BsonDocument {
{ "author", "Ernest Hemingway" },
{ "title", "For Whom the Bell Tolls" }
};
The compiler translates this into calls to the matching Add method:
BsonDocument book = new BsonDocument();
book.Add("author", "Ernest Hemingway");
book.Add("title", "For Whom the Bell Tolls");
A common mistake is to forget the inner set of braces. This will result in a compilation error. For example:
BsonDocument bad = new BsonDocument {
"author", "Ernest Hemingway"
};
is translated by the compiler to:
BsonDocument bad = new BsonDocument();
bad.Add("author");
bad.Add("Ernest Hemingway");
which results in a compilation error because there is no Add method that takes a single string argument.
Nested BSON documents are created by setting the value of an element to a BSON document. For example:
BsonDocument nested = new BsonDocument {
{ "name", "John Doe" },
{ "address", new BsonDocument {
{ "street", "123 Main St." },
{ "city", "Centerville" },
{ "state", "PA" },
{ "zip", 12345}
}}
};
This creates a top level document with two elements (name and address). The value of address is a nested BSON document.
BsonDocument has the following overloaded Add methods:
It is important to note that sometimes the Add methods do NOT add a new element. If the value supplied is null (or the condition supplied in the last overload is false) then the element isn’t added. This makes it really easy to handle optional elements without having to write any if statements or conditional expressions.
For example:
BsonDocument document = new BsonDocument {
{ "name", name },
{ "city", city }, // not added if city is null
{ "dob", dob, dobAvailable } // not added if dobAvailable is false
};
is more compact and readable than:
BsonDocument document = new BsonDocument();
document.Add("name", name);
if (city != null) {
document.Add("city", city);
}
if (dobAvailable) {
document.Add("dob", dob);
}
If you want to add a BsonNull if a value is missing you have to say so. A convenient way is to use C#’s null coalescing operator as follows:
BsonDocument = new BsonDocument {
{ "city", city ?? BsonConstants.Null }
};
The IDictionary overloads initialize a BsonDocument from a dictionary. Each key in the dictionary becomes the name of a new element, and each value is mapped to a matching BsonValue and becomes the value of the new element. The overload with the keys parameter lets you select which dictionary entries to load (you might also use the keys parameter to control the order in which the elements are loaded from the dictionary).
The recommended way to access BsonDocument elements is to use one of the following indexers:
Note that the return value of the indexers is BsonValue, not BsonElement. This actually makes BsonDocuments much easier to work with (if you ever need to get the actual BsonElements use GetElement).
We’ve already seen samples of accessing BsonDocument elements. Here are some more:
BsonDocument book;
string author = book["author"].AsString;
DateTime publicationDate = book["publicationDate"].AsDateTime;
int pages = book["pages", -1].AsInt32; // default value is -1
This class is used to represent BSON arrays. While arrays happen to be represented externally as BSON documents (with a special naming convention for the elements), the BsonArray class is unrelated to the BsonDocument class because they are used very differently.
BsonArray has the following constructors:
All the constructors with a parameter call the matching Add method. The multiple overloads are needed because C# does not provide automatic conversions from IEnumerable<T> to IEnumerable<object>.
BsonArray has the following Add methods:
Note that the Add method takes a single parameter. To create and initialize a BsonArray with multiple values use any of the following approaches:
// 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 };
Array elements are accessed using an integer index. Like BsonDocument, the type of the elements is BsonValue. For example:
BsonArray array = new BsonArray { "Tom", 39 };
string name = array[0].AsString;
int age = array[1].AsInt32;
Up until now we have been focusing on the BSON Library. The remainder of this tutorial focuses on the C# Driver.
Only a few of the C# Driver classes are thread safe. Among them: MongoClient, MongoServer, MongoDatabase, MongoCollection and MongoGridFS. Common classes you will use a lot that are not thread safe include MongoCursor and all the classes from the BSON Library (except BsonSymbolTable which is thread safe). A class is not thread safe unless specifically documented as being thread safe.
All static properties and methods of all classes are thread safe.
This class serves as the root object for working with a MongoDB server. The connections to the server are handled automatically behind the scenes (a connection pool is used to increase efficiency).
When you are connecting to a replica set you will still use only one instance of MongoClient, which represents the replica set as a whole. The driver automatically finds all the members of the replica set and identifies the current primary.
Instances of this class are thread safe.
By default and unless set otherwise, all operations requiring a WriteConcern use w=1. In other words, by default, all write operations will block until the server has acknowledged the write.
The easiest way to connect to a MongoDB server is to use a connection string. The standard connection string format is:
mongodb://[username:password@]hostname[:port][/[database][?options]]
The username and password should only be present if you are using authentication on the MongoDB server. These credentials will be the default credentials for all databases. To authenticate against the admin database append (admin) to the username. If you are using different credentials with different databases pass the appropriate credentials to the GetDatabase method.
The port number is optional and defaults to 27017.
To connect to multiple servers, specify the seed list by providing multiple hostnames (and port numbers if required) separated by commas. For example:
mongodb://server1,server2:27017,server2:27018
This connection string specifies a seed list consisting of three servers (two of which are on the same machine but on different port numbers). Because specifying multiple servers is ambiguous as to whether or not it is a replica set or multiple mongos (in a sharded setup), the driver will go through a discovery phase of connecting to the servers to determine their type. This has a little overhead at connection time and can be avoided by specifying a connection mode in the connection string:
mongodb://server1,server2:27017,server2:27018/?connect=replicaset
The available connection modes are automatic (the default), direct, replica set, and shardrouter. The rules for connection mode are as follows:
注解
If you have multiple servers listed, and one is part of a replica set and another is not, then the connection mode is non-deterministic. Be sure that you are not mixing server types on the connection string.
Should the connection mode resolve to a replica set, the driver will find the primary server even if it is not in the seed list, as long as at least one of the servers in the seed list responds (the response will contain the full replica set and the name of the current primary). In addition, other secondaries will also be discovered and added (or removed) into the mix automatically, even after initial connection. This will enable you to add and remove servers from the replica set and the driver will handle the changes automatically.
As alluded to above, the options part of the connection string is used to set various connection options. Suppose you wanted to connect directly to a member of a replica set regardless of whether it was the current primary or not (perhaps to monitor its status or to issue read only queries against it). You could use:
mongodb://server2/?connect=direct;readpreference=nearest
The full documentation for connection strings can be found at Connection String and read preferences at http://docs.mongodb.org/manual/applications/replication/#replica-set-read-preference.
Support for SSL is baked into the driver. You can configure this via the connection string be adding an ssl=true option to the options.
mongodb://server2/?ssl=true
By default, the server certificate will get validated against the local trusted certificate store. This sometimes causes issues in test environments where test servers don’t have signed certs. To alleviate this issue, you can also add an sslverifycertificate=false as another connection string option to ignore any certificate errors.
MongoDB supports a simple and straightforward authentication mechanism. You can read about it on the security and authentication docs page
The C# driver supports authentication in a couple of ways. As noted above in connection strings, you can specify default credentials on the connection string. The default credentials are always used as a fallback if no other credentials are supplied.
Supplying credentials can be done in two ways. First, they can be supplied to certain methods at runtime. These credentials will then be used to execute the desired functionality. The other, and more robust way, is to store credentials in a MongoCredentialsStore. MongoCredentials in the store are keyed by database, so if different databases require different users, then the credentials store is consulted first and, upon a miss, will fallback to the default credentials supplied on the connection string if they exist.
The example below uses the credential store to define admin credentials and credentials for the "foo" database. Access to databases other than "admin" or "foo" will use the connection string supplied default credentials "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);
You can navigate from an instance of a MongoClient to an instance of MongoServer by using the GetServer method.
The MongoServer class is used to provide more control over the driver. It contains advanced ways of getting a database and pushing a sequence of operations through a single socket in order to guarantee consistency.
You can navigate from an instance of MongoServer to an instance of MongoDatabase (see next section) using one of the following GetDatabase methods or indexers:
Sample code:
MongoClient client = new MongoClient(); // connect to localhost
MongoServer server = client.GetServer();
MongoDatabase test = server.GetDatabase("test");
MongoCredentials credentials = new MongoCredentials("username", "password");
MongoDatabase salaries = server.GetDatabase("salaries", credentials);
Most of the database settings are inherited from the server object, and the provided overloads of GetDatabase let you override a few of the most commonly used settings. To override other settings, call CreateDatabaseSettings and change any settings you want before calling GetDatabase, like this:
var databaseSettings = server.CreateDatabaseSettings("test");
databaseSettings.SlaveOk = true;
var database = server.GetDatabase(databaseSettings);
GetDatabase maintains a table of MongoDatabase instances it has returned before, so if you call GetDatabase again with the same parameters you get the same instance back again.
Sometimes a series of operations needs to be performed on the same connection in order to guarantee correct results. This is rarely the case, and most of the time there is no need to call RequestStart/RequestDone. An example of when this might be necessary is when a series of Inserts are called in rapid succession with a WriteConcern of w=0, and you want to query that data is in a consistent manner immediately thereafter (with a WriteConcern of w=0, the writes can queue up at the server and might not be immediately visible to other connections). Using RequestStart you can force a query to be on the same connection as the writes, so the query won’t execute until the server has caught up with the writes.
A thread can temporarily reserve a connection from the connection pool by using RequestStart and RequestDone. For example:
using(server.RequestStart(database)) {
// a series of operations that must be performed on the same connection
}
The database parameter simply indicates some database which you intend to use during this request. This allows the server to pick a connection that is already authenticated for that database (if you are not using authentication then this optimization won’t matter to you). You are free to use any other databases as well during the request.
RequestStart increments a counter (for this thread) which is decremented upon completion. The connection that was reserved is not actually returned to the connection pool until the count reaches zero again. This means that calls to RequestStart can be nested and the right thing will happen.
注解
RequestStart returns an IDisposable. If you do not use RequestStart with a using block, it is imperative that RequestDone be called in order to release the connection.
For a reference of other properties and method, see the api documentation.
This class represents a database on a MongoDB server. Normally there will be only one instance of this class per database, unless you are using different settings to access the same database, in which case there will be one instance for each set of settings.
Instances of this class are thread safe.
This method returns an object representing a collection in a database. When we request a collection object, we also specify the default document type for the collection. For example:
MongoDatabase hr = server.GetDatabase("hr");
MongoCollection<Employee> employees =
hr.GetCollection<Employee>("employees");
A collection is not restricted to containing only one kind of document. The default document type simply makes it more convenient to work with that kind of document, but you can always specify a different kind of document when required.
Most of the collection settings are inherited from the database object, and the provided overloads of GetCollection let you override a few of the most commonly used settings. To override other settings, call CreateCollectionSettings and change any settings you want before calling GetCollection , like this:
var collectionSettings = database.CreateCollectionSettings<TDocument>("test");
collectionSettings.SlaveOk = true;
var collection = database.GetCollection(collectionSettings);
GetCollection maintains a table of instances it has returned before, so if you call GetCollection again with the same parameters you get the same instance back again.
For a reference of other properties and method, see the api documentation.
This class represents a collection in a MongoDB database. The <TDefaultDocument> type parameter specifies the type of the default document for this collection.
Instances of this class are thread safe.
To insert a document in the collection create an object representing the document and call Insert. The object can be an instance of BsonDocument or of any class that can be successfully serialized as a BSON document. For example:
MongoCollection<BsonDocument> books =
database.GetCollection<BsonDocument>("books");
BsonDocument book = new BsonDocument {
{ "author", "Ernest Hemingway" },
{ "title", "For Whom the Bell Tolls" }
};
books.Insert(book);
If you have a class called Book the code might look like:
MongoCollection<Book> books = database.GetCollection<Book>("books");
Book book = new Book {
Author = "Ernest Hemingway",
Title = "For Whom the Bell Tolls"
};
books.Insert(book);
You can insert more than one document at a time using the InsertBatch method. For example:
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);
When you are inserting multiple documents InsertBatch can be much more efficient than Insert.
To retrieve documents from a collection use one of the various Find methods. FindOne is the simplest. It returns the first document it finds (when there are many documents in a collection you can’t be sure which one it will be). For example:
MongoCollection<Book> books;
Book book = books.FindOne();
If you want to read a document that is not of the <TDefaultDocument> type use the FindOneAs method, which allows you to override the type of the returned document. For example:
MongoCollection<Book> books;
BsonDocument document = books.FindOneAs<BsonDocument>();
In this case the default document type of the collection is Book, but we are overriding that and specifying that the result be returned as an instance of BsonDocument.
The Find and FindAs methods take a query that tells the server which documents to return. The query parameter is of type IMongoQuery. IMongoQuery is a marker interface that identifies classes that can be used as queries. The most common ways to construct a query are to either use the Query builder class or to create a QueryDocument yourself (a QueryDocument is a subclass of BsonDocument that also implements IMongoQuery and can therefore be used as a query object). Also, by using the QueryWrapper class the query can be of any type that can be successfully serialized to a BSON document, but it is up to you to make sure that the serialized document represents a valid query object.
One way to query is to create a QueryDocument object yourself:
MongoCollection<BsonDocument> books;
var query = new QueryDocument("author", "Kurt Vonnegut");
foreach (BsonDocument book in books.Find(query)) {
// do something with book
}
Another way to query is to use the Query Builder (recommended):
MongoCollection<BsonDocument> books;
var query = Query.EQ("author", "Kurt Vonnegut");
foreach (BsonDocument book in books.Find(query)) {
// do something with book
}
Yet another way to query is to use an anonymous class as the query, but in this case we must wrap the anonymous object:
MongoCollection<BsonDocument> books;
var query = Query.Wrap(new { author = "Kurt Vonnegut" });
foreach (BsonDocument book in books.Find(query)) {
// do something with book
}
If you want to read a document of a type that is not the default document type use the FindAs method instead:
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
}
The Save method is a combination of Insert and Update. If the Id member of the document has a value, then it is assumed to be an existing document and Save calls Update on the document (setting the Upsert flag just in case it actually is a new document after all). Otherwise it is assumed to be a new document and Save calls Insert after first assigning a newly generated unique value to the Id member.
For example, you could correct an error in the title of a book using:
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);
}
The TDocument class must have an Id member to be used with the Save method. If it does not you can call Insert instead of Save to insert the document.
The Update method is used to update existing documents. The code sample shown for the Save method could also have been written as:
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);
or using Query and 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);
Use FindAndModify when you want to find a matching document and update it in one atomic operation. FindAndModify always updates a single document, and you can combine a query that matches multiple documents with a sort criteria that will determine exactly which matching document is updated. In addition, FindAndModify will return the matching document (either as it was before the update or after) and if you wish you can specify which fields of the matching document to return.
Using the example documented here, findAndModify Command, the call to FindAndModify would be written in C# as:
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 is a way of a aggregating data from a collection. Every document in a collection (or some subset if an optional query is provided) is sent to the map function, which calls emit to produce intermediate values. The intermediate values are then sent to the reduce function to be aggregated.
This example is taken from page 87 of MongoDB: The Definitive Guide, by Kristina Chodorow and Michael Dirolf. It counts how many times each key is found in a collection.
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());
}
For a reference of other properties and method, see the api documentation.
The Find method (and its variations) don’t immediately return the actual results of a query. Instead they return a cursor that can be enumerated to retrieve the results of the query. The query isn’t actually sent to the server until we attempt to retrieve the first result (technically, when MoveNext is called for the first time on the enumerator returned by GetEnumerator). This means that we can control the results of the query in interesting ways by modifying the cursor before fetching the results.
Instances of MongoCursor are not thread safe, at least not until they are frozen (see below). Once they are frozen they are thread safe because they are read-only (in particular, GetEnumerator is thread safe so the same cursor could be used by multiple threads).
The most convenient way to consume the results of a query is to use the C# foreach statement. For example:
var query = Query.EQ("author", "Ernest Hemingway");
var cursor = books.Find(query);
foreach (var book in cursor) {
// do something with book
}
You can also use any of the extensions methods defined by LINQ for IEnumerable<T> to enumerate a cursor:
var query = Query.EQ("author", "Ernest Hemingway");
var cursor = books.Find(query);
var firstBook = cursor.FirstOrDefault();
var lastBook = cursor.LastOrDefault();
注解
In the above example, the query is actually sent to the server twice (once when FirstOrDefault is called and again when LastOrDefault is called).
It is important that a cursor cleanly release any resources it holds. The key to guaranteeing this is to make sure the Dispose method of the enumerator is called. The foreach statement and the LINQ extension methods all guarantee that Dispose will be called. Only if you enumerate the cursor manually are you responsible for calling Dispose.
A cursor has several properties that can be modified before it is enumerated to control the results returned. There are two ways to modify a cursor:
For example, if we want to skip the first 100 results and limit the results to the next 10, we could write:
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
}
or using the fluent interface:
var query = Query.EQ("status", "pending");
foreach (var task in tasks.Find(query).SetSkip(100).SetLimit(10)) {
// do something with task
}
The fluent interface works well when you are setting only a few values. When setting more than a few you might prefer to use the properties approach.
Once you begin enumerating a cursor it becomes “frozen” and you can no longer change any of its properties. So you must set all the properties before you start enumerating it.
The following properties of a cursor are modifiable:
The method names in parenthesis are the corresponding fluent interface methods.
The fluent interface also supports additional options that aren’t used very frequently and are not exposed as properties:
MongoCursor has a few methods used for some special purpose operations:
There are various levels of WriteConcern, and this class is used to represent those levels. WriteConcern applies only to operations that don’t already return a value (so it doesn’t apply to queries or commands). It applies to the following MongoCollection methods: Insert, Remove, Save and Update.
The gist of WriteConcern is that after an Insert, Remove, Save or Update message is sent to the server it is followed by a GetLastError command so the driver can verify that the operation succeeded. In addition, when using replica sets it is possible to verify that the information has been replicated to some minimum number of secondary servers.