MongoDB and C# Programming the Non-SQL Way

Introduction

MongoDB represents a new type of database model, one which turns the relational database design on its head. Relational databases, such as those in Microsoft SQL Server, MySQL or Oracle DB, store data in one or more structured tables with predefined columns. MongoDB does not store structured data, but rather free-form data known as a document in collections. Each document within a collection is free-form and may differ from all other documents. These collections are grouped together into databases.

Relational database primarily use the SQL language as the method for interacting with the data. The SQL language depends upon predefined structure (tables, fields, relationships, etc). Since MongoDB is entirely free-form it is not possible to use SQL. This also means that the client drivers for MongoDB will not be using the System.Data classes as these are designed for relational data sources. The open source community and supporters of MongoDB provide client libraries for the .NET framework. There are multiple client libraries available for use with MongoDB. The code in this article will be using the MongoDB-CSharp

First to we need to download the latest version of MongoDB from the MongoDB.org site and follow the directions to install and run it for the first time. Once MongoDB is up and running, it is not necessary to create any Databases as those will be created in code. Next download and unpack the client library from the MongoDB-CSharp site.

Next we can jump into C# programming and create the project. In order to access data within a MongoDB we first need to add a reference to the MongoDB.Driver assembly. Then we can start to add the base code to establish a connection to the server as listed below:

using (MongoDB.Driver.Mongo connection = new MongoDB.Driver.Mongo("Server=localhost:27017"))
{
   connection.Connect();

   MongoDB.Driver.Database db = connection["MongoDB_Example"];
   MongoDB.Driver.IMongoCollection ex_col = db["ex_col"];
}

The above code snippet first establishes a connection to the MongoDB server, then to the"MongoDB_Example" database and finally the "ex_col" collection. The remainder of the code snippets below will be inserted before the closing brace of the using statement above. It is also important to note that while we have established a connection to the database and the collection, nothing has been created at this point. The database and collection are created when the first document is inserted.

We now have enough information to go ahead and create the first MongoDB.Driver.Document and insert it into the collection. The following code snippet creates a simple document with a couple fields:

MongoDB.Driver.Document doc = new MongoDB.Driver.Document();
doc["First Name"] = "John";
doc["Last Name"] = "Doe";
ex_col.Insert(doc);

As you can see this code creates an empty document, then adds two fields, First Name and Last Name, then insert the documents into the collection. This same operation can be performed in a short hand format as shown below.

ex_col.Insert(new MongoDB.Driver.Document() {{"First Name","John"},{"Last Name","Doe"}});

The short hand format is especially useful for performing searches as shown later in this article. Before we start trying to find records it would be a good idea to populate the collection with a set of documents. The following code snippet creates 100 records with an id and a random number. This code also demonstrates the ability to add multiple documents with different fields.

Random rnd = new Random();

for (int i = 0; i < 100; i++)
{
   ex_col.Insert(new MongoDB.Driver.Document() { { "id", i }, { "value", rnd.NextDouble() } });
}

Now that we have a reasonable set of data to work with, we can start to perform searches upon that data. The following code block demonstrates the use for the collection FindAll method which is used to retrieve all documents within the collection.

using (MongoDB.Driver.ICursor cur = ex_col.FindAll())
{
   foreach (MongoDB.Driver.Document d in cur.Documents)
   {
      foreach (string s in d.Keys)
      {
         System.Diagnostics.Debug.Write(string.Format("({0} = {1}) ", s, d[s]));
      }
      System.Diagnostics.Debug.WriteLine("");
   }
}

The above code uses the FindAll method on the ex_col collection which returns an ICursor object to navigate through the records returned. The ICursor object provides a property Documents which provides the complete list of documents available. The code then loops through those documents and prints out the fields with values on a single line per document. When this code runs it will actually display 3 fields instead of the two we created previously. The third field _id is an autogenerated unique id for each document. This field can be useful when there is a need to uniquely refer to a single document.

While retrieving the complete list of documents is useful we still need the ability to perform a query. We can change the first line in the code block above to use the colletion Find method as shown below.

using (MongoDB.Driver.ICursor cur = ex_col.Find(new MongoDB.Driver.Document() { { "id", 3 } }))

The Find like the FindAll method returns an ICursor object; however, it accepts one document parameter. The document object used in this fashion provides a set of fields used to perform the search. This is known as a spec document and by default performs a direct match on all fields provided. This code will return all documents where the id field equals 3. It is also important to note that in MongoDB the value 3 and "3" are not equal as the types do not match. As you would expect, MongoDB also provides the ability to perform operators other than equal similar to SQL. The format is quite different from SQL as the operators must be included within the spec document used for search. The following example demonstrates how to use the < operator to return all documents where the id field is < 3.  Again we change line one from the code block above to change the search being performed.

using (MongoDB.Driver.ICursor cur = ex_col.Find(new MongoDB.Driver.Document() { { "id", new MongoDB.Driver.Document(){{"$lt", 3}} } }))

You should change the code above to replace the value of 3 with a document with a single field called $lt with the value 3. Please refer to the MongoDB.org site for a complete list of operators used in searches.

The above examples show how to return a list of documents; however, the driver also provides the ability to return a single document from a search as shown below.

MongoDB.Driver.Document oneDoc = ex_col.FindOne(new MongoDB.Driver.Document() { { "id", 3 } });

As you can see the FindOne method on the collection return a single document, which is very handy when you have a unique id you are searching by or for performing updates. The oneDoc returned in this code block or any other document from a Find or where the _id field has the unique id value set, it is possible to preform update operations upon. The following snippet show hows easy it is to perform an update on a document.

oneDoc["value"] = "13";
ex_col.Update(oneDoc);

By passing a document with changes to the Update method is very easy to commit the changes back to the collection.

Conclusion

As you can see from the sames above, MongoDB provides a very simple and powerful method of storing and accessing free form data. MongoDB is also very scalable and capable of running very large applications and websites. The abilty to store free-form data over Relational data can be an advantage for many applications and/or websites as it allow for greater flexibility without the need to modify the underlying database structure.





About the Author

Chris Bennett

Chris Bennett is a Manager with Crowe Horwath LLP in the Indianapolis office. He can be reached at chris.bennett@crowehorwath.com.