MongoDB, C# and Mono

MongoDB is one of the many free open source NoSQL databases that exist today. I wanted to try out how well the official drivers for C# worked when using Mono.

On the MongoDB web site they have pre built binaries of MongoDB for almost every platform, i grabbed the 64 bit binary for OSX. No installation is required, just unzip the file and run the “mongod” binary from the directory. There is also a binary called “mongo”, this is the mongo interactive shell which is very useful.

The C# drivers are available, both in binary form and as source, from the C# language center. To get started I fired up MonoDevelop 2.8 running on Mono 2.10.8:



MonoDevelop has matured a lot since I used it the first time. It is still not as good as Visual Studio, but it is getting there. What takes most time to get used to is the totally different set of shortcuts, and the lack of all ReSharper magic.

Let me break down a simple console application example on how to use MongoDB using the official driver from 10gen:

  1.  
  2.  
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using MongoDB.Bson;
  7. using MongoDB.Driver;
  8. using MongoDB.Driver.Builders;
  9.  
  10. namespace MongoDbTest
  11. {
  12.   public class Article
  13.   {
  14.     public MongoDB.Bson.ObjectId id { get; set; }
  15.     public string title { get; set; }
  16.     public string status { get; set; }
  17.  
  18.     public string[] tags { get; set; } 
  19.   }
  20.        
  21.   class MainClass
  22.   {
  23.     public static void Main (string[] args)
  24.     {
  25.       // Connect to the database and get a colleciton
  26.       MongoServer server = MongoServer.Create ("mongodb://localhost");
  27.       MongoDatabase db = server.GetDatabase ("mongodb-tutorial");
  28.                        
  29.       // Get an article collection
  30.       MongoCollection<BsonDocument> articles = db.GetCollection<BsonDocument> ("articles");
  31.                        
  32.       // Empty the collection (so all runs of this program will result in the same output)
  33.       articles.RemoveAll ();
  34.  
  35.  
  36.       //               
  37.       // Create two articles
  38.       //
  39.       articles.Save (new BsonDocument
  40.       {
  41.         {"title", "A MongoDB article"},
  42.         {"status", "published"},
  43.         {"tags", new BsonArray { "c#", "mongodb" }}     
  44.       });
  45.                        
  46.       articles.Save (new BsonDocument
  47.       {
  48.         {"title", "A Mono article"},
  49.         {"status", "draft"},
  50.         {"tags", new BsonArray { "c#", "mono" }}       
  51.       });              
  52.  

If you type this in the MongoDB shell, you will see that the following articles are stored in the database:

> db.articles.find()
        { 
          "_id" : ObjectId("4f097f8e74b5b343b97f56a7"), 
          "title" : "A MongoDB article", 
          "status" : "published", 
          "tags" : [ "c#", "mongodb" ] 
        }
				
        { 
          "_id" : ObjectId("4f097f8e74b5b343b97f56a8"), 
          "title" : 
          "A Mono article", 
          "status" : "draft", 
          "tags" : [ "c#", "mono" ] 
       }
  1.        
  2.      // Find and print the first article
  3.       BsonDocument firstArticle = articles.FindOne ();
  4.      
  5.       PrintArticle ( // Access the properties using indexing and then casting using the .As*
  6.         firstArticle["_id"].AsObjectId,
  7.         firstArticle["title"].AsString,
  8.         firstArticle["status"].AsString,
  9.         (firstArticle["tags"].AsBsonArray).Select (x => x.AsString));          
  10.      
  11.  
  12.       //
  13.       // Typed collections                     
  14.       //
  15.  
  16.       // Using indexing and the .As* operations is not that convenient. A better way
  17.       // is to use a typed collection. Earlier we defined a Article class, lets use it:
  18.       MongoCollection<Article> typedArticles = db.GetCollection<Article> ("articles");
  19.       Article typedArticle = typedArticles.FindOneAs<Article> ();
  20.      
  21.       PrintArticle (typedArticle.id, typedArticle.title,
  22.         typedArticle.status, typedArticle.tags);
  23.                        
  24.       // Save a typed article
  25.       typedArticles.Save (new Article {
  26.                                 title = "An article about MonoDevelop",
  27.                                 status = "published",
  28.                                 tags = new [] {"c#", "mono", "mono-develop"},
  29.                         });
  30.  
  31.  
  32.       //
  33.       // Querying
  34.       //
  35.                
  36.       // Find article with status draft
  37.       Article draftArticle = typedArticles
  38.         .FindOneAs<Article> (Query.EQ ("status", "draft"));
  39.      
  40.       PrintArticle (draftArticle.id, draftArticle.title,
  41.         draftArticle.status, draftArticle.tags);
  42.                        
  43.  
  44.       //
  45.       // Updating
  46.       //
  47.                
  48.       // Lets update the draft article and set status published
  49.       Console.WriteLine ("Nr of published articles before update:"
  50.                          + typedArticles.Find (Query.EQ ("status", "published")).Count ());
  51.                          
  52.       typedArticles.Update (
  53.         new QueryDocument { { "_id", draftArticle.id } },
  54.         new UpdateDocument { { "$set", new BsonDocument ("status", "published") }
  55.         // $set is atomic
  56.       });
  57.                
  58.       Console.WriteLine ("Nr of pubhlished articles after update:"
  59.                          + typedArticles.Find (Query.EQ ("status", "published")).Count ());
  60.                
  61.      
  62.       //
  63.       // MapReduce
  64.       //
  65.                
  66.       // This will count the number of times a tag has been used by using MapReduce
  67.       var map =
  68.         "function() {" +
  69.         "    this.tags.forEach(function(t) {" + // Iterate all the tags and
  70.         "        emit(t, { count : 1 }); " +    // emit the tag name and initial count
  71.         "    })" +
  72.         "}";
  73.                        
  74.       var reduce =
  75.         "function(key, emits) {" + // Reduce by tag name and summarize the result
  76.         "    total = 0;" +
  77.         "    for (var i in emits) {" +
  78.         "        total += emits[i].count;" +
  79.         "    }" +
  80.         "    return { count : total };" +
  81.         "}";
  82.  
  83.  
  84.       Console.WriteLine ("MapReduce result:");         
  85.       var mr = typedArticles.MapReduce (map, reduce);
  86.       foreach (var document in mr.GetResults()) {
  87.         Console.WriteLine (document.ToJson ());
  88.       }                
  89.     }
  90.                
  91.     public static void PrintArticle (ObjectId id, string title,
  92.       string status, IEnumerable<string> tags)
  93.     {
  94.       Console.WriteLine ("id: " + id + ", title: " + title);
  95.       Console.WriteLine ("- tags: " + string.Join (", ", tags));
  96.     }
  97.   }
  98. }
  99.  

Output from the program:

id: 4f0989fc74b5b3458ae38105, title: A MongoDB article
- tags: c#, mongodb
id: 4f0989fc74b5b3458ae38105, title: A MongoDB article
- tags: c#, mongodb
id: 4f0989fc74b5b3458ae38106, title: A Mono article
- tags: c#, mono
Nr of pubhlished articles before update:2
Nr of pubhlished articles after update:3
MapReduce result:
{ “_id” : “c#”, “value” : { “count” : 3.0 } }
{ “_id” : “mongodb”, “value” : { “count” : 1.0 } }
{ “_id” : “mono”, “value” : { “count” : 2.0 } }
{ “_id” : “mono-develop”, “value” : { “count” : 1.0 } }

Using the C# driver for MongoDB, is as you can see, very straight forward. Usually when I run applications on Mono I make sure to build all libraries using the Mono compiler, to make sure the Microsoft compiler have not used any optimizations that is not supported under Mono (I have run into this issue in the past). However, in this case the .NET binaries on the MongoDB site seems to work just fine under Mono. I also downloaded the MongoDB C# driver source and build them using MonoDevelop without any problems.

One improvement I would like to see is LINQ support in the driver. It would be so much more convenient to be able to use LINQ and strongly typed objects to query the database or create MapReduce-functions.

1 comment on “MongoDB, C# and Mono

  1. Thanks for this. It saved me a few days floundering around to try and get things working.

    Very clear instructions easy to follow, even for Java type like me.

Submit comment

Allowed HTML tags: <a href="http://google.com">google</a> <strong>bold</strong> <em>emphasized</em> <code>code</code> <blockquote>
quote
</blockquote>