Simple-MongoDB – Simplified storage of entities

New features

Just checkedin some new features to my C# 4.0 based MongoDB driver:

  • New simplified API, using generics and adds pluralization for collection
  • Added SimoIoC which will resolve resources like pluralizer, commands etc. this lets you replace certain parts of the implementation with own implementations.
  • Support for document references via the new type SimoReference

All code is written for .Net 4.0 and is tested against MongoDB v.1.2.4

All of the examples shown here can be found in the Test-projects, and most of them in the class “ApiExamples” (no I don’t generally comment my tests as I have done in this class).

The code can be found here: http://code.google.com/p/simple-mongodb/

Ok, but why Simple-MongoDB?

Before showing you the code for these new features, I thought that I should take some time to explain why I develope Simple-MongoDB when there’s good alternatives, both Sam Corder and Rob Conery & CO have their versions and it doesn’t stop there.

So, I started writing it beacause I wanted to learn more about MongoDB and because I didn’t like the tight coupling to a certain Document-type that Sam had in his driver. I wanted a driver that accepted everything seamlessly. I driver where I could use plain static types (e.g. Person-class) as well as anonymous types and Json-strings. And I don’t want the user to be writing one single line of mapping code. Thats why Simple-MongoDB.

New simplified API – Using generics with an existing model

If you do have a model built in C# code you would probably feel most comfortable with using the EntityStore-generic API. By doing this you don’t have to use strings to specify collections. Each class-type will be stored in a collection with the same name as the type, but pluralized. If you don’t want yo use pluralization, you can turn it off either by calling session.Pluralizer.Disable() or by replacing the implementation of the SimoIoC.Instance.PluralizerFactory.

The hierarchy in MongoDB is:

- Db
  - Collection
    - Document

By using EntityStore this is somewhat abstracted away.

[TestMethod]
public void Insert_Example()
{
    //You do need a connection to create a session
    //The creating of Connections and Session is something you most likely
    //will put in a IoC-container or factory or something.
    var cn = CreateConnection();
    using (var session = new SimoSession(cn))
    {
        var entityStore = new SimoEntityStore(session, DbName);

        //If you are dealing with typed documents (e.g. Person)
        //you can use the generic API.
        //Then the name of the collection is the name of the
        //type, but pluralized.
        //If you don't want pluralization, call
        //session.SimoPluralizer.Disable(), or
        //replace the implementation of the 
        //SimoIoC.Instance.PluralizerFactory
        var person = new Person { Name = "Daniel", Age = 29 };
        entityStore.Insert(person);

        //If you are using non-typed documents you have to pass
        //the entityname string to the method you are using.
        var anonymousPerson = new { Name = "Daniel" };
        entityStore.Insert("Person", anonymousPerson);

        //So the EntityStore only allows for an abstraction
        //over the Db, Collection and Document hierarchy
        //and you can of course access these to.
        var db = session[DbName];
        var persons = db["Person"];
        persons.Insert(anonymousPerson);

        //The EntityStore also holds the Database that it wraps.
        //Of course you can obtain a collection using generics against the
        //database.
        var persons2 = entityStore.Database.GetCollection<Person>();
        persons2.Insert(person);

        var numOfStoredDocuments = entityStore.Count<Person>();
        Assert.AreEqual(4, numOfStoredDocuments);
    }
}

Document references

I will let the code speak for itself.

[TestMethod]
public void ParentChildReference_Example()
{
    var cn = CreateConnection();
    using (var session = new SimoSession(cn))
    {
        var entityStore = new SimoEntityStore(session, DbName);

        //The parent generates a new _id when created.
        //That _id is then used in the reference which is attached to the child.
        //After that, you just store the items.
        var parent = new Parent { Name = "Daniel" };
        var fatherReference = entityStore.Reference<Parent>(parent._id);
        var child = new Child { Name = "Isabell", Father = fatherReference };

        //You could of course have created the reference manually, but then you loose the
        //use of the pluralizer, and have to role-this on your own.
        //new SimoReference { CollectionName = "Parents", Id = parent._id };

        entityStore.Insert(parent);
        entityStore.Insert(child);

        var refetchedChild = entityStore.FindOne<Child>(new { child._id });

        Assert.AreEqual(fatherReference.Id, refetchedChild.Father.Id);
        Assert.AreEqual(fatherReference.CollectionName, refetchedChild.Father.CollectionName);
    }
}

If you're not working with the EntityStore but instead with the SimoCollections directly, it would look something like this (from "ParentChildTests"):

[TestMethod]
public void NewRelation_UsingAnonymousTypes_ReferenceCreated()
{
    var parentId = SimoId.NewId();
    var parent = new { _id = parentId, Name = "Daniel" };
    var fatherReference = new SimoReference
    {
        CollectionName = "Parents",
        Id = parent._id
    };
    var child = new { _id = SimoId.NewId(), Name = "Isabell", Father = fatherReference };
    InsertDocuments("Parents", parent);
    InsertDocuments("Childs", child);

    var refetchedChild = GetDocument(new { child._id }, child, "Childs");

    Assert.AreEqual(fatherReference.Id, refetchedChild.Father.Id);
    Assert.AreEqual(fatherReference.CollectionName, refetchedChild.Father.CollectionName);
}

Session factory

So there's a verry simple SessionFactory inplace, but you can of course make one of your own or let an IoC like StructureMap resolve the sessions.

[TestMethod]
public void SessionFactory_Example()
{
    var sessionFactory = new SimoSessionFactory();
    using (var session = sessionFactory.GetSession(Constants.ConnectionStringName))
    {
        session[DbName].DropDatabase();
    }
}

Pluralization

The Pluralizer is created per Session and is enabled by default. You can of course disable it, and you can constrol how certain values are pluralized. If you want to provide a own implementation you have to adjust this via SimoEngine.IoC.PluralizerFactory.

[TestMethod]
public void DisablePluralizer_Example()
{
    var cn = CreateConnection();
    using (var session = new SimoSession(cn))
    {
        //Just call Disable on the Pluralizer
        //When you want to enable it again, call
        //Enable().
        var entityStore = new SimoEntityStore(session, DbName);
        entityStore.Session.Pluralizer.Disable();

        var person = new Person { Name = "Daniel" };
        entityStore.Insert(person);

        var refetched = entityStore.Database["Person"].FindOne<Person>(new { person.Name });

        Assert.IsNotNull(refetched);
    }
}

You can adjust how certain values should be pluralized.

[TestMethod]
public void AdjustPluralization_IsCustomPluralized()
{
    var pluralizer = new SimoPluralizer();

    pluralizer.AdjustPluralization("Monkey", "Test");

    Assert.AreEqual("Test", pluralizer.Pluralize("Monkey"));
}

That's it for now. Not sure what I will look into next.

//Daniel

About these ads

15 thoughts on “Simple-MongoDB – Simplified storage of entities

  1. Hi Daniel,

    Thanks for the follow ups on your driver status. One quick question, is it possible to intercept the persistence of an entity in a transparent manner so that for example say you have the following class:

    public class Person {
    public Person Parent {get;set;}
    }

    Now, on persistence, instead of storing the Parent as an embeded object, store it as a call it dbref or just the _id, then use Dyna Proxy or some other “proxifier” then perform lazy loading of the parent property. Was thinking more along the lines of collections, say public IList Children {get;set;}, more than specific properties. But thats the idea.

    Was thinking that the “Simple” method would be to have a sor of market interface which would mark the entity as a candidate for its own collection, eg:

    public interface IStoreable{ } or something.

    Hope this isn’t some wierd dilusion or something :)

    • Hi,
      So, I think this is a result of me building the EntityStore where I kind of “hide” the document-thinking a bit in MongoDB. What I mean is that I guess that document-storage probably make use of nesting (which is supported) more than relations, but of course this is a very good feature to build in. As a first step I was thinking of implementing like MongoDB’s Fetch() on the relation, but I guess I might as well start working on eager and lazy loading of references objects, when you are using the EntityStore API. I will probably not be finished tomorrow, though ;)

      //Daniel

  2. Cool. The best starting point would probably be to check out how nhibernate does this. Although nh doesn’t use any marker interface as you have to define the relation between entities before hand. But yeah, something along those lines, and maybe even run into a EntityRelation Map thingy would probably be a good bet and would eliminate the need for the marker interface.

    I’ll give it a try and see if I can get something started aswell, to see how it looks and then if I manage i’ll send you the code. ^^

  3. Pingback: DotNetShoutout

  4. Pingback: iAwaaz-News-by-People

    • The real thank you to that should go to Newtonsoft Json.Net :)

      I sat down last night fiddling with having an SimoEntityReference. I can create an Eager loading option without having any proxy. All I have to do is keep track of the metadata of the entities (eg. using AppDomain) and if it’s an entity with references and the Session has Eagerloading = true, these EntityReferences could be loaded with the referenced entity.

      Feedback?

      //Daniel

  5. Hi Daniel,

    How would you write a $where statement… say something like this:

    (from the mongo console)
    db.ColName.find({ $where : function() { foo == 1 && bar == 2 } })

    ?

    • Hi,
      You use the Find-method in the EntityStore-API or the Session-API, and write like this:

      //Find(@"{$where : ""this.Name.indexOf('Daniel') &gt; -1""}");
      //Find(@"{$where : ""function() { return this.Name.indexOf('Daniel') &gt; -1;}""}");
      //Find(new WhereOperator(@"this.Name.indexOf('Daniel') &gt; -1"));
      

      //Daniel

  6. That looks interesting
    :)

    In regard to the domain model, would it be possible to specify the ID field as any type instead of as a SimoId?

    • Hi,

      As of right now the _id used in conjunction with the references needs to be of SimoObjectId. Except from that you can use:

      public class Person
      {
          public Guid _id { get; set; }
      }
      

      I can look at making it work with custom id's even with references.

      But why not see the _id as DB-specific and instead focus on the business-keys?

      //Daniel

    • Fixed support for Guid, int, long and string. I hope it’s enough for now. Please provide feedback of how you would like to use it.

      //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