Getting started with MongoDB – Using Json.Net and Castle Dynamic proxy

This post is divided in three blocks:
– Getting MongoDB to run on your machine
– Consume it manually via the MongoDB – Console
– Consume it from C# code.

Updates!

If you are just intereseted in the C# implementation, the section for how to consume it manually using the console, can be skipped.

The section where I show you how to use it from C# code contains examples of how to get it to work using either Json.Net or Castle Dynamic proxy.

Note! The compiled libs of the MongoDB-driver (mongodb-csharp) has one tweak that I have applied. I have updated the ToString implementation of the class “Oid”, so that it returns a correct Json-format.

Getting MongoDB to run on your machine

Step 1 – Download the binaries

Go to MongoDB – downloads

I selected the 64-bit version for Windows. There is a limitations of a maximum size of 2gb per database if you use 32-bit.

Unzip and put the binaries where you want them.

My selection: “C:\MongoDB\Binaries”

Step 2 – Create the data folder

We need to manually create the folder which MongoDB will use as its storage area. By default this is: “c:\data\db” and you must create this manually. If you want to customize this, which I want, you have to execute the binary “mongod.exe” and provide it the switch “-dbpath”. I will also like it to have one directory per database that is being created, hence I use the switch “-directoryperdb”.

More about switches could be found here.

The account that is executing the mongod.exe, must have read and write access to the data folder.

My selection: “C:\MongoDB\Data”

Step 3 – Start “mongod.exe”

Start the “server” so that the core is up and running so that you can connect to it and start storing data.

So my exe is located under: C:\MongoDB\Binaries\, hence my command looks like
C:\MongoDB\Binaries>mongod -dbpath c:\MongoDB\Data -directoryperdb

Step 4 – Verfiy that Mongo is up and running

I will do this manually, using the consoleclient “mongo.exe”. It’s located with the other binaries. Just fire it up using the command line and you will be seing:

Which confirms that the server is up. Initially there are some default databases created, and as you can see one of them are “test”.

Consume MongoDB manually via the MongoDB – Console

Step 1 – Store some data using the console

Ok, lets just create a simple object with three properties: Title, Body, Tags; and lets store it under the collection “Notes” which in turn is stored in the database “SimpleNotes”:

The conceptual model will look something like this:

SimpleNotes
	Notes
		Title
		Body
		Tags
			Tag#1
			Tag#2
			...		

Start the console (mongo.exe) and type in the following commands:


use SimpleNotes
db.Notes.save({ Title: "The first document", Body: "My first document stored in MongoDB.", Tags: ["MongoDB", "Getting started"]})
db.Notes.save({ Title: "The second document", Body: "My second document stored in MongoDB."})

Read more about inserting.

Step 2 – Reconnect and query the SimpleNotes-database

Lets intend that we are opening the console and have forgotten database name etc. If you are still connected, type exit so that we can simulate a clean session. Then start mongo.exe again and exeute the following commands:

show dbs
use SimpleNotes
show collections

You will find our created database “SimpleNotes” (which is created automatically when first used) and you will also find the “Notes” collection.

Step 7 – List the stored items

Lets query out our two stored notes.

db.Notes.find()

You should now be presented with two stored documents. One that has a property “Tags” and one that doesn’t.

Step 10 – Query for specific attribute

Lets find the document that has the Tag “Getting started”.


db.Notes.find({Tags : "Getting started"})

Read more about querying

Consume MongoDB from C# code

Step 1 – Get C# drivers for building a custom client

First you need to download a driver for C#. Go to http://github.com/samus/mongodb-csharp

I downloaded the ZIP (click “Download source”).

Unzip and open the Visual Studio solution and compile it. After having updated the ToString implementation in the “Oid” class, I took the two dll’s:
MongoDB.Driver.dll
MongoDB.Linq.dll

and I put them under: “C:\MongoDB\Drivers\csharp”. You can put them anywhere. You are just going to use “Add reference from within Visual studio”.

Step 2 – Get Json.Net

I’m using Json.Net for serialization/deserialization. You can download it from here.

Step 3 – Build the C# client

For simplicity I just created a simple console application using Visual Studio 2010. I have provided three different ways, showning you have to consume MongoDB via the C#-driver:
– Using Json
– Using Serialization/Deserialization in Json.Net
– Using Castle Dynamic proxy

All three cases will look like this: Store two notes, one with Tags and one without. The one without will then be refetched and updated with a Tag.

Fix Oid – ToString

To get things to work I had to do some tweaking. At first I hade to ensure that ToString in the class “Oid” returned a Json-representation that I code consume with Json.Net.

public override string ToString()
{
    //Old: return string.Format(@"ObjectId(""{0}"")", BitConverter.ToString(value).Replace("-","").ToLower());
    return string.Format("\"{0}\"", BitConverter.ToString(value).Replace("-","").ToLower());
}

Using Json

Lets look at the consuming code. Do you remember the notation in MongoDB? Database, Collections and Documents. The Document is what contains the datastructure, which isn’t the same as a traditional row, since the schema doesn’t have to be equal for all the documents in the same collection. Each document can further contain other documents or references to other documents (will be covered in future writings).

I have built some helper methods to map between the “Document” and the C# entity class “Note”. These methods are placed in the helper class: “MongoJson”.

var json = new MongoJson();

//Connect to server
var mongo = new Mongo();
mongo.Connect();

//Create clean database
var db = mongo["SimpleNotes"];
db.SendCommand("dropDatabase");

//Get collection "Notes" to hold our Note-documents
var notes = db["Notes"];

//Dump to console to see that the database is empty.
PrintNotes("Initial notes", notes);

//Create and Insert first note with properties:
//Title, Body, Tags-array
var firstNoteDocument = 
	json.DocumentFrom("{
		Title : \"First note using Json.\",
		Body : \"Some nice text.\",
		Tags : [\"MongoDB\", \"Getting started\"] }");
		
notes.Insert(firstNoteDocument);

PrintNotes("After first insert using Json", notes);

//Create and Insert a second note with no Tags
//This note will not have Tags-represented in the schema.
var secondNoteDocument =
	json.DocumentFrom("{
		Title : \"Second note using Json.\", 
		Body : \"Some nice text.\"}");
		
notes.Insert(secondNoteDocument);

PrintNotes("After second insert using Json", notes);

//Read back the second note that lacked tags and provide one
var noteDocument = notes.FindOne(new Document { { "Tags", MongoDBNull.Value } });

//Update the fetch object with values from another document
//(merge of members/values)
noteDocument.Update(json.DocumentFrom("{Tags : [\"The tag\"]}"));

//Update in Db
notes.Update(noteDocument);

PrintNotes("After update of post with empty tags, using Json", notes);

mongo.Disconnect();

Using Serialization/Deserialization in Json.Net

Now I will have a static C# representation of my Note-entity.

public interface IMongoEntity
{
    string _id { get; set; }
    Oid GetOid();

    Document GetAsDocument();
    void UpdateFromDocument(Document document);
}

[Serlializable]
public class Note
    : IMongoEntity
{
    public virtual string _id { get; set; }
    public virtual string Title { get; set; }
    public virtual string Body { get; set; }
    public virtual string[] Tags { get; set; }

    public virtual Oid GetOid()
    {
        return new Oid(_id);
    }

    public virtual Document GetAsDocument()
    {
        throw new NotImplementedException();
    }

    public virtual void UpdateFromDocument(Document document)
    {
        throw new NotImplementedException();
    }
}

The members GetAsDocument() and UpdateFromDocument() isn’t used in this example. They are used when we use Castle Dynamic proxy.

The consuming code will now use Document when communicating with MongoDB and will use Note in the application/domain. To map between them I will make use of Json.Net. I will only show the parts that are different this time. The consuming code looks like this:

//Create new C# Note and convert it to a document using JSON serialization/deserialization
var firstNote = new Note {
	Title = "First note using Serialization",
	Tags = new string[] { "MongoDB", "Getting started" },
	Body = "Some nice text." };
	
//Convert Note to Document and insert it
var firstNoteDocument = json.DocumentFrom(firstNote);
notes.Insert(firstNoteDocument);

...

//Create and Insert second note
var secondNote = new Note { 
	Title = "Second note using Serialization.", 
	Body = "Some nice text." };
	
var secondNoteDocument = json.DocumentFrom(secondNote);
notes.Insert(secondNoteDocument);

...

//Read back the second note that lacked tags.
var noteDocument = notes.FindOne(new Document { { "Tags", MongoDBNull.Value } });
var note = json.ObjectFrom<Note>(noteDocument);

note.Tags = new[] { "The tag" };

//Populate the document with the changed C# object, and update in MongoDB.
json.PopulateDocumentFrom(noteDocument, note);
notes.Update(noteDocument);

Ok, time to look at the helper class. There’s no actual magic there. Just using Json.Net for serialization and deserialization.

To go from a Document to a Note I just deserialize the JSON representation of the document, which now works since I fixed the Oid-class (read about it above).

To go from a Note to a Document I need to get the JSON representation of the Note and then deserialize this to a Dictionary with key-value objects, which then are looped and assigned to the Document.

public class MongoJson
{
    private const string _oidContainerName = "_id";

    public T ObjectFrom<T>(Document document)
        where T : class, IMongoEntity
    {
        if (document == null)
            return null;

        return JsonConvert.DeserializeObject<T>(document.ToString());
    }

    public Document DocumentFrom(string json)
    {
        return PopulateDocumentFrom(new Document(), json);
    }

    public Document DocumentFrom<T>(T item)
        where T : class, IMongoEntity
    {
        return PopulateDocumentFrom(new Document(), item);
    }

    public Document PopulateDocumentFrom<T>(Document document, T item)
        where T : class, IMongoEntity
    {
        if (item == null)
            return document;

        var json = JsonConvert.SerializeObject(item, Formatting.None);

        return PopulateDocumentFrom(document, json);
    }

    private Document PopulateDocumentFrom(Document document, string json)
    {
        var keyValues = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);

        foreach (var keyValue in keyValues)
        {
            var isEmptyKeyField = (
                                      keyValue.Key == _oidContainerName && document[_oidContainerName] != MongoDBNull.Value);

            if (isEmptyKeyField)
                continue;

            var value = keyValue.Value ?? MongoDBNull.Value;

            if (value != MongoDBNull.Value)
            {
                var arrayValue = (keyValue.Value as JArray);
                if (arrayValue != null)
                    value = arrayValue.Select(j => (string)j).ToArray();
            }

            if (document.Contains(keyValue.Key))
                document[keyValue.Key] = value;
            else
            {
                if (value != MongoDBNull.Value)
                    document.Add(keyValue.Key, value);
            }
        }

        return document;
    }
}

Using Castle Dynamic proxy

One implementation left. This time I will use Castle Dynamic proxy to map between Document and Note. I do this by intercepting the properties of Note and store them in a simple statebag. The statebag is then used when converting between Notes and Documents.

First the consuming code that are different than before.

var proxyBuilder = new ProxyBuilder(new ProxyConfig());

//Create and Insert first note
var firstNote = proxyBuilder.ProxyFromClass<Note>(new EntityInterceptor());
firstNote.Title = "First note using Proxies.";
firstNote.Tags = new string[] { "MongoDB", "Getting started" };
firstNote.Body = "Some nice text.";

notes.Insert(firstNote.GetAsDocument());

...

//Create and Insert second note
var secondNote = proxyBuilder.ProxyFromClass<Note>(new EntityInterceptor());
secondNote.Title = "Second note using Proxies.";
secondNote.Body = "Some nice text.";

notes.Insert(secondNote.GetAsDocument());

...

//Read back the second note that lacked tags.
var noteDocument = notes.FindOne(new Document { { "Tags", MongoDBNull.Value } });
var note = proxyBuilder.ProxyFromClass<Note>(new EntityInterceptor());

note.UpdateFromDocument(noteDocument);

note.Tags = new[] { "The tag" };

//Populate the document with the changed C# object, and update in MongoDB.
notes.Update(note.GetAsDocument());

The interceptor that does the work, looks like this.

public class EntityInterceptor
    : IInterceptor
{
    private Dictionary<string, object> _stateBag = new Dictionary<string, object>();
    private static readonly Type _documentType = typeof (Document);

    public void Intercept(IInvocation invocation)
    {
        var name = invocation.MethodInvocationTarget.Name;

        if(IsProperty(name))
        {
            var key = invocation.MethodInvocationTarget.Name.Remove(0, 4);

            if (IsSetter(name))
                SetValue(key, invocation.Arguments[0]);
            else if (IsGetter(name))
            {
                var value = GetValue(key);
                if (value != null)
                    invocation.ReturnValue = value;
            }
        }
        else if(name == "GetAsDocument")
        {
            var document = new Document();

            foreach (var keyValue in _stateBag)
            {
                document.Add(keyValue.Key, keyValue.Value);
            }

            invocation.ReturnValue = document;
        }
        else if (name == "UpdateFromDocument")
            SetValuesFrom((Document)invocation.Arguments[0]);
        else
            invocation.Proceed();
    }

    private void SetValuesFrom(Document document)
    {
        foreach (DictionaryEntry keyValue in document)
            SetValue((string)keyValue.Key, keyValue.Value);
    }

    private void SetValue(string key, object value)
    {
        if(!_stateBag.ContainsKey(key))
            _stateBag.Add(key, value);
        else
            _stateBag[key] = value;
    }

    private object GetValue(string key)
    {
        if (!_stateBag.ContainsKey(key))
            _stateBag.Add(key, null);

        return _stateBag[key];
    }

    private bool IsProperty(string name)
    {
        return (name.StartsWith("set_") || name.StartsWith("get_"));
    }

    private bool IsGetter(string name)
    {
        return name.StartsWith("get_");
    }

    private bool IsSetter(string name)
    {
        return name.StartsWith("set_");
    }
}

That’s it. As always all the code can be downloaded from here.

//Daniel

About these ads

18 thoughts on “Getting started with MongoDB – Using Json.Net and Castle Dynamic proxy

  1. Pingback: DotNetShoutout

  2. Nice Post

    Do you have plans to implement with Mongo in the near future?

    I am working on some proof of concept work at the moment, testing Mongo for a variety of persistence concerns and was planning on using interception (probably using Castle proxies), however in a different approach. If validated by the POC we will likely use Mongo in systems where publish subscribe and durable messaging are employed heavily. I blogged about it here:

    http://www.simonsegal.net/blog/2010/01/26/finding-my-way-with-mongodb-and-c-part-10/

    • I’will use it in some personal applications. As I’m working as a consultant where I feel that the organisations are more comfortable with administrating/supporting a RDMS like SQL Server, I think it will take some time before I get the chance to use it in a business case.

      //Daniel

  3. Pingback: MongoDB in C# – Extensions to support Json-mapping or Proxy generation « Daniel Wertheim

  4. Hi,

    Tried out your example but for some reason the call to the ObjectFrom method keeps throwing an exception. The stack trace is quite long but all related to Newtonsoft. The relevant lines are:

    at System.Number.StringToNumber(String str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo info, Boolean parseDecimal)

    at System.Number.ParseInt64(String value, NumberStyles options, NumberFormatInfo numfmt)
    at System.Convert.ToInt64(String value, IFormatProvider provider)

    at Newtonsoft.Json.JsonTextReader.ParseNumber()

    • Hi!
      I have sent you an email so that you can send me your code.

      Do that and I will check it out.

      //Daniel

  5. Yes thanks Daniel, it works nicely now. The problem apparently is that I was using the driver from git which retrieves the oids as numbers.

    Now, regarding the storing of arrays,

    In the
    protected virtual object ConvertToMongoDbValue(JToken token)
    if (token.Type == JTokenType.Array)
    {
    var r = token.Select(j => (string)j).ToArray();
    if (r == null || r.Length < 1)
    return MongoDBNull.Value;

    return r;
    }

    You could change it to:

    if (value.Type == JTokenType.Array) {
    var items = new List();

    foreach (var v in value) {
    var doc = PopulateDocumentFrom(new Document(), v.ToString());
    items.Add(doc);
    }

    if (items.Count < 1)
    return MongoDBNull.Value;

    return items.ToArray();
    }

    Giving you the ability to store/retrieve nested types :)

    • Hi!

      The issue has been corrected in Oid.ToString in the MongoDb-driver, but Sam was uncertain if it had reached the main-branch at Github.

      Nice tip with the nested types.

      //Daniel

  6. No there is only one other problem with the ToString method on the document… It takes into consideration the current culture, which in my case has “,” as the decimal separator and this obviously screws up the json format :/

  7. There is also an error when trying to serialize an object property which is not a simple type in the ToString method. It will generate the property as

    “Property” : “{” “Value”: 1, “Other”: 2 “}”

    Instead of:

    “Property” : { “Value”: 1, “Other”: 2 }

    I’ll post these as issues to git hub. For the time being i’ve used:

    private static string ToInvariantString(this Document doc) {
    var json = new StringBuilder();
    json.Append(“{ “);
    var first = true;

    foreach (string key in doc.Keys) {
    if (first) {
    first = false;
    } else {
    json.Append(“, “);
    }
    json.AppendFormat(CultureInfo.InvariantCulture, @”””{0}””: “, key);
    SerializeType(doc[key], json);
    }
    json.Append(” }”);
    return json.ToString();
    }

    private static void SerializeType(object value, StringBuilder json) {
    if (value == null) {
    json.Append(“null”);
    return;
    }
    var t = value.GetType();
    if (value is bool) {
    json.Append(((bool)value) ? “true” : “false”);
    } else if (t.IsArray) {
    json.Append(“[ ");
    var first = true;
    foreach (var v in (Array)value) {
    if (first) {
    first = false;
    } else {
    json.Append(", ");
    }
    SerializeType(v, json);
    }
    json.Append(" ]“);
    } else if (value is Document) {
    json.Append(((Document)value).ToInvariantString());
    } else if (value is Oid ||
    value is int ||
    value is Int32 ||
    value is long ||
    value is float ||
    value is double) {
    json.AppendFormat(CultureInfo.InvariantCulture, “{0}”, value);
    } else if (value is DateTime) {
    json.AppendFormat(@”””{0}”””, ((DateTime)value).ToUniversalTime().ToString(“o”));
    } else {
    json.AppendFormat(CultureInfo.InvariantCulture, @”””{0}”””, value);
    }
    return;
    }

    Which is about the same as the .ToString method on the document, with a few edits here and there :)

    Hope this helps someone.

  8. Pingback: My opinion on mappings for MongoDB « Daniel Wertheim

  9. Pingback: Simple-MongoDB – Part 1, Getting started « Daniel Wertheim

  10. Pingback: Getting started with MongoDB – Using Json.Net and Castle Dynamic proxy « Daniel Wertheim | Head.SmackOnTable();

    • This is an old post, the official driver that now exists has it’s own BSON serializer.

      //Daniel

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s