Click here to Skip to main content
Click here to Skip to main content


, 9 Jan 2013
Rate this:
Please Sign up or sign in to vote.


This article attempts to highlight the latest developments in both the Mongo open-source document database and the open-source official C# driver and to supplement the previous reviews on CodeProject in the light of these improvements.

Overview of Document Databases.

Document databases store information relating to a record in a contiguous blob of data known as a document . A document’s structure usually follows the JSON format and consists of a series of key-value pairs. Unlike the schema of relational databases, the document’s structure does not reference empty fields. This flexible arrangement allows fields to be added and removed with ease. What’s more, there is no need to rummage about in various tables when trying to assemble the data; it’s all there in one solid block. The downside of all this is that Document databases tend to be meaty. But, now that disk drives are in the bargain basement, the trade off between speed of access and storage costs has shifted in favour of speed and that has given rise to the increased use of document databases. The Large Hadron Collider at Cern uses a document database but that's not why it keeps breaking down.

Hosted Web Server for MongoDb.

There is free web hosting of MongoDb at MongoHQ . The sandbox database plan provides 512mb of storage and is a good way to test drive the database. There is no need to download the mongoDb binaries and the web site’s user interface provide allows administrative tasks to be carried out. Just sign up, down load the driver and you’re cooking with gas. I’ve used this service, it seems to be genuinely free and there is no badgering to upgrade.

Desktop Installation of MongoDb and the C# Driver

All you need to get started is on the Mongodb website. Installation instructions are well documented although you might have to wade through some detritus to get the correct set for your system. You also need to download the C# .Net driver as well as the MongoDb binaries. The C# Driver consists of two libraries: the BSON Library, MongoDB.Bson.dll, and the C# Driver, MongoDB.Driver.dll. There is a basic user interface situated 1000 ports above the port the database listens on. For the default installation, this is at http://localhost:28017/. You need to have the line 'Set rest=true' in the mongod.cfg file to enable this interface. There are also more sophisticated open-source applications available for carrying out administration tasks on the database.

The Database Structure

The basic structure for storing data fields is the BsonElement. It’s a simple KeyValue pair . The Key contains a field name and the Value its value. The Value can itself be a BsonElement, so they can be nested, Russian doll style. Records are stored as documents. The Document is a collection of BsonElements. Here is an example document.

 _id : 50e5c04c0ea09d153c919473,
  Age : 43,
 Cars : {0:Humber,1: Riley},
 Forename : Rhys,
 Lastname : Richards


Every record does not need to contain every field. The only required field is the _id and fields can be added at a future date without having to change the existing records. In this example, the Cars field is an array. Its Value field contains a nested Document. The elements in the nested Document are KeyValue pairs. The key is the array index number and the value is the name of the car.

The C# driver.

The driver is used to interface your code to a Mongo database. The driver can serialize data classes to the database without the need for special attributes. All that's required is a unique Id. This is usually of type BSON.ObjectId, a 12 byte time-stamped value which is automatically assigned by Mongodb . You can use a GUID instead but it needs to be mapped to a string. The reason for this is that a GUID is usually stored as a binary and the driver’s Aggregation Framework has problems digesting binary data. I get the same sort of trouble with cucumber sandwiches.

Connecting to the database.


const string hostedWebConnectionString = 

The default connection string for the desktop server is simply mongodb://localhost. Here is the code for accessing a database named test.

const string connectionString = "mongodb://localhost";

// Create a MongoClient object by using the connection string
var client = new MongoClient(connectionString);

//Use the MongoClient to access the server
MongoServer server = client.GetServer();

// Use the server to access the 'test' database
MongoDatabase database = server.GetDatabase("test");


Accessing collections.

Documents with a similar structure are arranged as named collections of data in the database. The driver has a Collection object that acts as a proxy for a database’s collection. The following code shows how to access and enumerate a collection, named 'entities', of type ClubMember.

//Builds new Collection if 'entities' is not found
MongoCollection<ClubMember> collection = database.GetCollection<ClubMember>("entities");

MongoCursor<ClubMember> members = collection.FindAll();
foreach (ClubMember clubMember in members)

It’s recommended that the foreach method is used wherever possible as it cleans up after itself. Boring housekeeping duties such as calling AttachDatabase(),DropDatabase() are a thing of the past. You should avoid calling DropDatabase() as it closes down the database's connection pool.



//Build an index if it is not already built

//Add an optional name- useful for admin
IndexOptionsBuilder options = IndexOptions.SetName("myIndex");

//This locks the database while the index is being built
collection.EnsureIndex(keys, options);


using (server.RequestStart(database))

Querying Data Using Linq.

This is done by referencing the Collection’s AsQueryable method before writing the Linq statements All the usual methods are available. Here are a few examples

var names =
    collection.AsQueryable().Where(p => p.Lastname.StartsWith("R") && p.Forename.EndsWith("an")).OrderBy(
        p => p.Lastname).ThenBy(p => p.Forename).Select(p => new { p.Forename, p.Lastname });

foreach (var name in names)
    Console.WriteLine(name.Lastname + " " + name.Forename);

var regex = new Regex("ar");
IQueryable<string> regexquery =
    collection.AsQueryable().Where(py => regex.IsMatch(py.Lastname)).Select(p => p.Lastname).Distinct();

foreach (string name in regexquery)

Querying Data Using The QueryBuilder Class.


DateTime membershipDate = DateTime.Now.AddYears(-5);

DateTime membershipDateUTC = membershipDate.ToUniversalTime();

//Query.GT implements a 'greater than' query.
// The parameters are a field name and its Value 

MongoCursor<ClubMember> recentMembers =
collection.Find(Query.GT("MembershipDate", membershipDateUTC));
foreach (ClubMember clubMember in recentMembers)

There are methods to carry out most of the common sorts of comparisons.The Query.And method does a logical AND on successive Query objects. The next bit of code illustrates this by finding all members called David Jones and then updating the Forename to Dai. The Update.Set() method sets the Forename field to its new value on all documents selected. Finally , Collection.Update performs the update on the server side.

//Change the name of every David Jones to Dai Jones
IMongoQuery davidJonesQuery = Query.And(Query.EQ("Lastname", 
collection.Update(davidJonesQuery, update, UpdateFlags.Multi);

Querying Data Using Map Reduce.


Querying Data Using The Aggregation Framework.

The Aggregation Framework is used to collect and collate data from various documents in the database. It’s new in version 2.2 and is an attempt to bring the functionality of SQL to a document database. The aggregation is achieved by passing a collection along a pipeline where various pipeline operations are performed consecutively to produce a result. It’s an oven-ready chicken type production line -there is less product at the end but it is more fit for purpose. Aggregation is performed by calling the Collection’s Aggregate method with an array of documents that detail various pipeline operations.

Aggregation Example.


public class ClubMember
    #region Public Properties
    public int Age { get; set; }
    public List  <string> Cars { get; set; }
    public string Forename { get; set; }
    public ObjectId Id { get; set; }
    public string Lastname { get; set; }
    public DateTime MembershipDate { get; set; }

    #region Public Methods and Operators

    public void PrintDetailsToScreen()
                         this.Lastname, this.Forename, 
                         this.Age, this.MembershipDate.ToShortDateString()));


The ClubMember Class has an array named Cars that holds the names of the vintage cars owned by the member. The aim of the aggregation is to produce a list of owners who have joined in the last five years for each type of car in the collection.

Step 1 Match Operation.

The match operation selects only the members that have joined in the last five years. Here's the code.


var matchMembershipDateOperation = new BsonDocument

As you can see, the code ends up with more braces than an orthodontist but at least itelliSense assists when you are writing it. The keyword $gte indicates a greater than or equal query.

Step 2 Unwind Operation.



Becomes 3 documents


If there are two or more identical elements, say Evans has two MGs, then there will be duplicate documents produced. Unwinding an array makes its members accessible to other aggregation operations.


Step3 Group Operation.

Define an operation to group the documents by car type. Each consecutive operation does not act on the original documents but the documents produced by the previous operation. The only fields available are those present as a result of the previous pipeline operation. You can not go back and pinch a field from the original documents. The $ sign is used in two ways. Firstly, to indicate a keyword and, secondly, to differentiate field names from field values. For example, Age is a field name, $Age is the value of the Age field.

var groupByCarTypeOperation = new BsonDocument
        //Sort the documents into groups
        new BsonDocument
            // of a field named Car.
            // Set its value to that of the Cars field
            // The Cars field is nolonger an array because it has now been unwound
                //Add a field named Owners
                new BsonDocument
                        //add a value to the Owners field if it does not
                        //already contain an  identical value.
                        //This makes the field Value an array
                        // a serialized ClubMember class.
                        new BsonDocument
                            { "_id", "$_id" },
                            { "Lastname", "$Lastname" },
                            { "Forename", "$Forename" },
                            { "Age", "$Age" },

Step 4 Project Operation.

The _id field resulting from the previous operation is a BsonElement consisting of both the field name and its Value. It would be better to drop the field name and just use the Value. The following Project operation does that.

var projectMakeOfCarOperation = new BsonDocument
        "$project", new BsonDocument
            { "_id", 0 },
            { "MakeOfCar", "$_id.Car" },
            { "Owners", 1 }

Step 5 Sort Operation.

Define an operation to Sort the documents by car type.



Step 6 Run the Aggregation and output the result.

AggregateResult result = collection.Aggregate(

The AggregateResult class returned has a bool field named Ok. It is set to true if there were no errors. The resulting documents are returned in the AggregateResult.ResultDocuments collection. The easiest way to deserialize the collection is to call its Select method passing in the Deserialize method of the BsonSerializer as follows.

public class CarStat
#region Public Properties

public string MakeOfCar { get; set; }

public BsonDocument[] Owners { get; set; }


IEnumerable<CarStat> carStats = 
foreach (CarStat stat in carStats)
    IEnumerable<ClubMember> clubMembers =
        stat.Owners.AsEnumerable().Select(BsonSerializer.Deserialize<ClubMember>).OrderBy(p => p.Lastname).
            ThenBy(p => p.Forename).ThenBy(p => p.Age).Select(p => p);

    foreach (ClubMember member in clubMembers)

The sample application has an aggregation example that performs various calculations on the data set such as Count, Min, Max and Total.


GridFS is a means of storing and retrieving files that exceed the BsonDocument size limit of 16MB. Instead of storing a file in a single document, GridFS divides a file into chunks and stores each of the chunks as a separate document. GridFS uses two collections to store files. One collection stores the file chunks and the other stores the file’s metadata. The chunk size is about 256k. The idea here is that smaller chunks of data can be stored more efficiently and consume less memory when being processed than large files. It’s generally not a good idea to store binary data in the main document as it takes up space that is best used by more meaningful data. Uploading data into GridFs is straight forward. Here are a couple of examples.


MongoGridFSFileInfo gridFsInfo = database.GridFS.Upload(fullyQualifiedUpLoadName);

   gridFsInfo= database.GridFS.Upload(fs, "mars.png");

The GridFS.Upload method returns an object of type MongoGridFSFileInfo. This contains the file’s metadata. Only basic details such as the file’s name and length are included by default but the metadata can be customised to facilitate searching. Here's how.

BsonDocument photoMetadata = new BsonDocument
//Get the collection of metadata
var coll= database.GetCollection("fs.files");

//Build an index using the customised metadata fields.

//Find files using the indexed metadata fields

//use the GridFSFileInfo object to download the file
database.GridFS.Download(fullyQualifiedDownLoadName, gridFsInfo);

//Delete all files in the Astronomy category      

MongoDB Replica Sets.

A replica set is a cluster of mongoDB instances that replicate amongst one another so that they all store the same data. One server is the primary and receives all the writes from clients. The others are secondary members and replicate from the primary asynchronously. The clever bit is that, when a primary goes down, one of the secondary members takes over and becomes the new primary. This takes place totally transparently to the users and ensures continuity of service. Replica sets have other advantages in that it is easy to backup the data and databases with a lot of read requests can reduce the load on the primary by reading from a secondary. You can not rely on any one instance being the primary as the primary is determined by members of the replica set at run time.

Installing A Replica set as a Windows Service.

This example installs a replica set consisting of one primary and two secondary instances. The instances will be name MongDB0. MongoDB1, MongoDB2. They will use IP address localhost and listen on ports 27017, 27018 and 27019 respectively. The replica set name is myReplSet.

Step 1 Housekeeping tasks.

In the mongodb folder add three new folders named rsDataDb0, rsDataDb1, rsDataDb2. These are the data folders. Remove any instance of mongo that may be already running. In this example the service name to be removed is MongoDB. Open a command prompt in administrator mode, navigate to where mongod.exe is installed and enter:

mongod.exe --serviceName MongoDB  --remove

Step 2 Install three new service instances.


logappend = true
journal = true
quiet = true
# The port number the mongod server will listen on
# change port for each server instance
# Listen on a specific ip address
# Keep same replica set for all servers in the set




net start MongoDB0
net start MongoDB1
net start MongoDB2

Step 3 Configure the Replica Set.

To configure the Replica set you need to use the Mongo shell. Make sure you are in the \mongodb\bin directory and enter the command

mongo MongoDB0



Pass this variable to the rs.initiate() method by entering the command


You now have time to put the kettle on while Mongo takes your hard drive for a spin. When the method returns you are ready to go. You can find out the status of your replica set by entering rs.status() in the mongo shell. To connect to the Replica set with the C# driver use this connection string.

const string connectionString = 


There is much more to MongoDB than is detailed in this article but the hope is that there is enough information here for you to be able to begin exploring the capabilities of this open source software. Finally, I’d like to express my gratitude to the many developers who have worked tirelessly on the MongoDB  project with little prospect of reward other than the satisfaction of having helped others. Thanks very much  –I take my hat off to you.



About the Author

George Swan
Wales Wales
No Biography provided

Comments and Discussions

QuestionExpected element name to be '_t', not '0'. [modified] Pinmemberchutya1127-Dec-13 8:32 
QuestionEntity Classes for MongoDB PinmemberSahilj8813-Dec-13 4:12 
AnswerRe: Entity Classes for MongoDB PinmemberGeorge Swan13-Dec-13 22:08 
QuestionMy vote of 5 PinmemberTrollTier7-Aug-13 22:58 
AnswerRe: My vote of 5 PinmemberGeorge Swan8-Aug-13 12:03 
QuestionVote of 5 PinmemberGanesanSenthilvel15-Jul-13 10:36 
AnswerRe: Vote of 5 PinmemberGeorge Swan17-Jul-13 6:50 
GeneralMy vote of 5 PinprofessionalRob Philpott26-Jun-13 11:19 
GeneralRe: My vote of 5 PinmemberGeorge Swan26-Jun-13 19:34 
GeneralMy vote of 5 PinmemberAmund Gjersøe25-Feb-13 1:58 
GeneralRe: My vote of 5 PinmemberGeorge Swan1-Mar-13 9:08 
QuestionThis looks great but... PinmemberSorenDalby9-Jan-13 23:06 
AnswerRe: This looks great but... PinmemberGeorge Swan10-Jan-13 0:22 
Mongo is widely deployed commercially Soren. But I don’t think it is a suitable replacement for a SQL database in every case. It would seem to me that the greatest advantage is the clean, simple structure and the ability to add additional fields with ease. As with most open source software, you may have to hunt around to find the tools you need. They are out there but they are not well collated. Thanks and best wishes.
GeneralMy vote of 5 Pinmemberdinhienhy9-Jan-13 19:35 
GeneralRe: My vote of 5 PinmemberGeorge Swan10-Jan-13 0:39 
GeneralMy vote of 5 PinmemberPCoffey9-Jan-13 10:35 
GeneralRe: My vote of 5 PinmemberGeorge Swan10-Jan-13 0:37 
GeneralMy vote of 5 PinmvpFlorian Rappl9-Jan-13 8:04 
GeneralRe: My vote of 5 PinmemberGeorge Swan10-Jan-13 0:33 



| Advertise | Privacy | Mobile
Web03 | 2.8.140709.1 | Last Updated 9 Jan 2013
Article Copyright 2013 by George Swan
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid