Quick Start

MongoDB.Dynamic requires that your application targets .NET 4.0 that supports the dynamic keyword.
The easist way to start using MongoDB.Dynamic is with the NuGet package.
Open the NuGet package manager from VIsual Studio 2010 IDE and search for “MongoDB.Dynamic”.
Or in the Package Manager Console, type “Install-Package MongoDB.Dynamic”.
The MongoDB.Dynamic NuGet package has dependencies on mongocsharpdriver and ImpromptuInterface, packages that will be installed automatically.

1 - Configuring Connection Strings

The first thing to do is configure the connection string for the application.
The default connection string name that MongoDB.Dynamic will use is “MongoServerSettings”.
After installing the nuget package, the package will put an App.Config file automatically. Adjust the connection string according to your environment (server, dbname):

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <add name="MongoServerSettings" connectionString="mongodb://localhost/$rootnamespace$?safe=true" />
  </connectionStrings>
</configuration>


To override the name of default connection string you must use the static method Dynamic.Configure:

using MongoDB.Dynamic;

        [ClassInitialize]
        public static void Initialize(TestContext ctx)
        {
            Dynamic.Configure(connStringOrName: "MyDefaultConnStrName");
        }

2 - Setting up Entities

MongoDB.Dynamic will persist only instances based on interfaces, the “schema” will be defined by the public properties found on each interface.
For example:
using System.Collections.Generic;

namespace MongoDynamic.Tests
{
    public interface ICustomer
    {
        int Id { get; set; }
        string Name { get; set; }

        IEnumerable<IOrder> Orders { get; set; }
    }

    public interface IOrder
    {
        int Id { get; set; }
        ICustomer Customer { get; set; }
        int IdCustomer { get; set; }
    }
}


Based on above code, the database will contains two collections: ICustomer and IOrder.
The collection ICustomer will contains documents that will have two fields: Id and Name. The property Orders that is an IEnumerable of IOrder, will be ignored when persisting. By convention, all properties of IEnumerable will not persist. This property will be used later when querying for customers when configured to do this (Eager loading).
/* 0 */
{
  "_id" : 1,
  "Name" : "Jone"
}

The collection IOrder will also contains documents that will have two fields: Id and IdCustomer. The property Customer will be ignored because MongoDB.Dynamic will assume that is a foreign key of ICustomer.
/* 0 */
{
  "_id" : 1,
  "IdCustomer" : 1
}


3 - CRUD operations

CRUD is simple. And with MongoDB.Dynamic is even simpler.
Given the interfaces representing our entities, we can create instances of that interfaces with MongoDB.Dynamic.
First we need to get an instance of a DynamicCollection. DynamicCollection is a class that wraps a MongoCollection using dynamic C# to serialize interfaces.
    /* this will return a DynamicCollection instance that we can use to CRUD for customers */
    DynamicCollection<ICustomer> customers = Dynamic.GetCollection<ICustomer>();


    /* this will return a DynamicCollection instance that we can use to CRUD for orders */
    DynamicCollection<IOrder> orders = Dynamic.GetCollection<IOrder>();


Now that we have the collection available, let’s do some CRUD:
    ICustomer customer = customers.New(); //ImpromptuInterface returns a proxy instance o ICustomer
    customer.Name = "John"; //set the name property
    customers.Upsert(customer); //Update or Insert based on key value. Key is 0, so the customer will be inserted into collection.

    var orderA = orders.New(); //creating a new order
    orderA.IdCustomer = customer.Id; //setting IdCustomer
    orders.Upsert(orderA); //Insert order

/* after Upsert, the customer and order objects will have the Id field filled automatically */
    Assert.IsTrue(customers.All().Any(c => c.Id == customer.Id));

Methods exposed by DynamicCollection:
All()
IEnumerable<ICustomer> allCustomers = customers.All();

CollectionQuery()
IEnumerable<ICustomer> results = customers.CollectionQuery("Name", ExpressionType.Equal, "nameToquery");

Count()
long count = customers.Count();

Delete()
bool wasDeleted = customers.Delete(customers.GetFirstOrDefault());

DeleteByKey()
bool wasDeletedByKey = customers.DeleteByKey(1);

GetByKey()
ICustomer cust = customers.GetByKey(1);

GetFirstOrDefault()
ICustomer cust = customers.GetFirstOrDefault(c => c.Name == "name");

New()
ICustomer newCustomer = customers.New();

RemoveAll()
customers.RemoveAll();

Upsert()
customers.Upsert(newCustomer);

4 - Indexes

By default, as you know, MondoDB always creates an unique Index for _id field.
In case you wish to create/ensure indexes with MongoDB.Dynamic, there’s a method you can use:
Dynamic.Config.SetIndex<ICustomer>("idx_name", true, c => c.Name);

The signature method is defined:
public static void SetIndex<TContract>(string indexName, bool isUnique, params Expression<Func<TContract, object>>[] expressions)
{
    ...
}

The first parameter is the index name, the second a boolean indicating wheter the index will be created as unique, and the third parameter is an array of Expression<Func<T,object>> that will be use to get strongly type field names that will be part of the index.

5 - Global Context

The best way to ensure that all configurations will work as expected is to create a static class that centralize all your entities configurations and initialize the static contructor at some startup point of the application.
public static class MongoDBContext
    {
        public static void Initialize()
        {
           //dummy method just to force the execution of the static constructor.
           //call this method from App initialization
        }

        static MongoDBContext()
        {
            Dynamic.Configure(notifyProperyChanged: true);

            Dynamic.Config.SetKeyName<ICustomer>(c => c.Id);
            Dynamic.Config.SetKeyName<IOrder>(o => o.Id);
            Dynamic.Config.SetIndex<ICustomer>("idx_customer", false, cust => cust.Name);
            Dynamic.Config.LoadFK<IOrder, ICustomer>(order => order.Customer, order => order.IdCustomer);
            Dynamic.Config.LoadCollection<ICustomer, IOrder>(c => c.Orders, o => o.IdCustomer);
        }

        public static DynamicCollection<ICustomer> Customers
        {
            get { return Dynamic.GetCollection<ICustomer>(); }
        }

        public static DynamicCollection<IOrder> Orders
        {
            get { return Dynamic.GetCollection<IOrder>(); }
        }
    }


Then call MongoDBContext.Initialize() from WPF Application Startup or global Web Application StartUp.
public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            MongoDBContext.Initialize(); //call initialize will fire the static constructor

            base.OnStartup(e);
        }
    }

6 - Eager Loading

Configuring automatic eager loading
To configure ICustomer to load all related IOrder entities when it’s loaded, it’s necessary to setup the keys and properties so MongoDB.Dynamic will look for it’s configuration at runtime and will pre-load the related entities. Note that MongoDB.Dynamic will do extra queries, so do not abuse of this functionality.
Dynamic.Config.LoadCollection<ICustomer, IOrder>(customer => customer.Orders, order => order.IdCustomer);

This code it’s telling to MongoDB.Dynamic: “Every time you load an instance of ICustomer, please fill the property Orders with it’s related orders. The value of the current customer is in the field IdCustomer”.
Dynamic.Config.LoadFK<IOrder, ICustomer>(order => order.Customer, order => order.IdCustomer);

This means: “Every time you load an instance of IOrder, please fill the property Customer, querying the collection ICustomer for the customer that has the value of IdCustomer”.

7 - Conventions

a) MongoDB.Dynamic only works with interfaces when creating collections. For example, if you call:

var customerCollection = Dynamic.GetCollection<Customer>();

This will throw an exception, because when MongoDB.Dynamic constructs an instance of DynamicCollection, it will check if the typeof(T) is an interface. In case of not an interface type, the constructor will fail.
This is because by convention MongoDB.Dynamic will inspect properties of an interface.
This possibilites that you create your own dto’s if needed, but the DTO’s must implements an interface.

b) Primary keys: If a primary key field is defined as Int32, then MongoDB.Dynamic automatically will increment Id’s during insert.

It will create a collection named LastId and will store the last inserted Id for each collection.
The primary key of a collection can be determined by configuration or by convention.
/* 0 */
{
  "LastId" : 1,
  "_id" : "ICustomer"
}

/* 1 */
{
  "LastId" : 1,
  "_id" : "IOrder"
}

To speed up creation of a collection, it’s recommended to setup each Id for each collection you want to use with MongoDB.Dynamic.

Dynamic.Config.SetKeyName<ICustomer>(c => c.Id);

If you not setup this, then MongoDB.Dynamic will search for a property Id via reflection.
If a property Id is not found, then will use the first property found on the interface.

8 - Data Transfer Objects

It is possible to create Data Transfer objects and pass it to the DynamicCollection in order to persist it.
For example, if you have an ICustomer interface, you can create an concrete class that implements ICustomer and put extra-fields with properties / whatever you wish.
    public interface ICustomer
    {
        int Id { get; set; }
        string Name { get; set; }

        IEnumerable<IOrder> Orders { get; set; }
    }

    public interface IOrder
    {
        int Id { get; set; }
        ICustomer Customer { get; set; }
        int IdCustomer { get; set; }
    }
    public class Customer : ICustomer
    {
        public int Id { get; set; }
        public string Name { get; set; }     
        public string ExtraIgnoredField { get; set; } /* an extra field that doesn't exists in interface ICustomer */

        public IEnumerable<IOrder> Orders { get; set; }
    }

When you call Upsert, only the properties of the interface will be saved, extra fields will be ignored.

        [TestMethod]
        public void DataTransferObjectTeste()
        {
            var customers = Dynamic.GetCollection<ICustomer>();

            var customer = new Customer(); // implements ICustomer
            customer.Name = "John";
            customer.ExtraIgnoredField = "some ignored value";

            customers.Upsert(customer);

            Assert.IsTrue(customer.Id > 0);
        }

9 - INPC (INotifyPropertyChanged)

Thanks to ImpromptuInterface, it’s easy to add autonotification of property changes to instances of entities based on interfaces.
This is fully supported in MongoDB.Dynamic.
This behavior by default is disabled, since in web applications this is not commonly used.
But in WPF/Windows Forms, this is very useful.
To enable it, simply call the configure method:
using MongoDB.Dynamic;

            Dynamic.Configure(notifyProperyChanged: true);


        [TestMethod]
        public void NotifyPropChanged()
        {
            var customers = Dynamic.GetCollection<ICustomer>(); //get the collection

            var cust = customers.New(); //create a new customer

            var notify = cust as INotifyPropertyChanged; //verify if implements INotifyPropertyChanged

            Assert.IsNotNull(notify); //check if not null

            notify.PropertyChanged += (s, e) =>
                                          {
                                              Assert.IsNotNull(e.PropertyName);
                                          }; //anonymous delegate

            cust.Name = "John";
        }

Last edited Jun 8, 2012 at 1:12 PM by jpolvora, version 4

Comments

No comments yet.