Showing posts with label mongodb. Show all posts
Showing posts with label mongodb. Show all posts

Sunday, 1 September 2013

C# MongoDB Repository Implementation

I recently started a small project with a friend and we opted for MongoDB as our data store. One of the initial tasks was to write a repository class that allowed us to store, retrieve, update and search for entities in MongoDB. In the past, I've worked on codebases where there was one repository per entity, it didn't take long to discover that this resulted in a lot of code duplication across the data layer. So the goal here was to write the repository generically enough so that we only have one implementation that can be reused for any entity within our application. This meant that each entity would have its own MongoDB collection (if you're not familiar with MongoDB, a collection can be thought of as a table in the relational world - see this page for a comparison).

One of the first steps in this task was to write an interface for the repository - this is in case we decide to use a different data store in the future. A while back, I found a repository interface on the Redmondo blog which covers everything I'd want from a repository, so we'll use a slightly modified version of that interface (I've removed comments for brevity, you can download the code using a link further below and that'll contain the comments):
public interface IRepository<TEntity> where TEntity : EntityBase
{
    bool Insert(TEntity entity);
    bool Update(TEntity entity);
    bool Delete(TEntity entity);
    IList<TEntity>
        SearchFor(Expression<Func<TEntity, bool>> predicate);
    IList<TEntity> GetAll();
    TEntity GetById(Guid id);
}
To paraphrase the code, this is a generic repository interface for an entity of type TEntity that must derive from a base class called EntityBase. The EntityBase class is a very simple abstract class and contains just one property, the identifier property:
/// <summary>
/// A non-instantiable base entity which defines 
/// members available across all entities.
/// </summary>
public abstract class EntityBase
{
    public Guid Id { get; set; }
}
The idea is for any entity that we want to manage in our data store, that entity must derive from EntityBase. So the infrastructure is all in place for our "MongoDbRepository". I used the official 10gen MongoDB C# driver (available as a NuGet package) and arrived at the following repository implementation:
/// <summary>
/// A MongoDB repository. Maps to a collection with the same name
/// as type TEntity.
/// </summary>
/// <typeparam name="T">Entity type for this repository</typeparam>
public class MongoDbRepository<TEntity> :
    IRepository<TEntity> where
        TEntity : EntityBase
{
    private MongoDatabase database;
    private MongoCollection<TEntity> collection;

    public MongoDbRepository()
    {
        GetDatabase();
        GetCollection();
    }

    public bool Insert(TEntity entity)
    {
        entity.Id = Guid.NewGuid();
        return collection.Insert(entity).Ok;
    }

    public bool Update(TEntity entity)
    {
        if (entity.Id == null)
            return Insert(entity);
       
        return collection
            .Save(entity)
                .DocumentsAffected > 0;
    }

    public bool Delete(TEntity entity)
    {
        return collection
            .Remove(Query.EQ("_id", entity.Id))
                .DocumentsAffected > 0;
    }

    public IList<TEntity>
        SearchFor(Expression<Func<TEntity, bool>> predicate)
    {
        return collection
            .AsQueryable<TEntity>()
                .Where(predicate.Compile())
                    .ToList();
    }

    public IList<TEntity> GetAll()
    {
        return collection.FindAllAs<TEntity>().ToList();
    }

    public TEntity GetById(Guid id)
    {
        return collection.FindOneByIdAs<TEntity>(id);
    }

    #region Private Helper Methods
    private void GetDatabase()
    {
        var client = new MongoClient(GetConnectionString());
        var server = client.GetServer();

        database = server.GetDatabase(GetDatabaseName());
    }

    private string GetConnectionString()
    {
        return ConfigurationManager
            .AppSettings
                .Get("MongoDbConnectionString")
                    .Replace("{DB_NAME}", GetDatabaseName());
    }

    private string GetDatabaseName()
    {
        return ConfigurationManager
            .AppSettings
                .Get("MongoDbDatabaseName");
    }

    private void GetCollection()
    {
        collection = database
            .GetCollection<TEntity>(typeof(TEntity).Name);
    }
    #endregion
}
In case you're interested, a while ago I wrote a separate blog post on how to perform CRUD operations against MongoDB using the C# driver. To use the repository implementation, you'll need two application configuration settings defined - one that stores the name of the MongoDB database and the other that contains the MongoDB connection string (with a placeholder for the database name). You should have something like:
<appSettings>
  <add key="MongoDbDatabaseName" value="MyCarsDatabase" />
  <add key="MongoDbConnectionString"
         value="mongodb://localhost/{DB_NAME}?safe=true" />
</appSettings>
Hopefully this repository is useful to someone else who is working with MongoDB in C#. Any questions or suggestions for improvements are always welcome via comments. The source code is available on GitHub - https://github.com/rsingh85/MongoDbRepository.

Tuesday, 18 December 2012

MongoDB Developer Course Success

A bit off topic today, just some good news. In an earlier post, I mentioned that I was on a MongoDB developer course (ran by 10gen, the MongoDB company). Last week was the final week of the course with the final exam deadline this Monday. The results have just been published - I received a grade of 85% so have officially passed the course (certificates are awarded from 65% onwards).

The course has given me an excellent grounding in MongoDB and I just wanted to thank 10gen for administrating it. It looks like they are running a Java version of the course next year, so I highly recommend any developers interested in MongoDB to enrol onto it.

It's a nice way to end a busy year. Merry Christmas and Happy New Year.

Saturday, 24 November 2012

CRUD against MongoDB via C#

In my previous post, I gave some of my first thoughts about NoSQL in relation to MongoDB. In this post we will look at how you can perform basic CRUD operations against a MongoDB collection using the official C# driver. The purpose of this post is for me to use it as a handy reference when working with MongoDB in the future - hopefully you'll also find it useful. I will assume that the reader has some familiarity with using MongoDB on the shell.

Throughout the post I'll be using a simple collection of documents where each document stores some basic information about an aircraft. If you load the MongoDB shell and execute the following statements then we'll have a common ground to start with:

db.planes.insert({
    'Manufacturer':'Lockheed Skunk Works',
    'Model':'Blackbird SR-71',
    'FirstFlight': new Date('12/22/1964')
})
db.planes.insert({
    'Manufacturer':'Lockheed Skunk Works',
    'Model':'X-55 ACCA',
    'FirstFlight': new Date('06/02/2009')
})
At this point, you should have a database called mongotest with one collection containing two JSON documents. You can verify this by executing:
db.planes.find().pretty()
We'll now access and modify the planes collection using the C# driver. You can download the driver from here or add it to your solution using the NuGet package (it's easy to do using the NuGet package manager in Visual Studio). For this post, I'm using version 1.6.1 of the official MongoDB C# driver. Assuming you now have added references to MongoDB.Bson.dll and MongoDB.Driver.dll, add the following three using statements at the top of your class (FYI, I practiced with a standard console application directly within the Main method):
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
Now we can define a class that will map to a single plane document. We will use this class along with the MongoDB generic C# driver methods to insert, retrieve, update and remove documents from our planes collection.
public class Plane
{
    public ObjectId Id { get; set; }
    public string Manufacturer { get; set; }
    public string Model { get; set; }
    public DateTime FirstFlight { get; set; }
}
Note how we're using the MongoDB.Bson.ObjectId type for our identifier property (we're relying on MongoDB to add unique id's to newly inserted plane documents). Also note that the Plane class must support a parameterless constructor for the driver to be able to work with it.

Let's now take a look at how you can insert a new plane document into the planes collection using the driver. We'll first get a handle on the planes collection, I have the following four lines at the top of my Main method that gives me the required reference (planesCollection):
var connectionString = "mongodb://localhost/?safe=true";
var server = MongoServer.Create(connectionString);
var mongotestdb = server.GetDatabase("mongotest");
var planesCollection = mongotestdb.GetCollection<Plane>("planes");
To insert a new document into the planes collection (note that the Id property will be filled-in by the insert method):
planesCollection.Insert(new Plane {
    Manufacturer = "Unknown",
    Model = "Tornado IDS/ECR",
    FirstFlight = new DateTime(1974, 8, 14)
});
To retrieve a list of all the documents in the planes collection:
var planes = planesCollection.FindAll().ToList();
To retrieve a list of filtered documents - let's say we want only the planes that are manufactured by Lockheed:
var lockheedPlanes = planesCollection.Find(
    Query.EQ("Manufacturer""Lockheed Skunk Works"))
        .ToList();
To update a document (as a whole block) in the planes collection:
var planeToUpdate = planesCollection.FindOne(
    Query.EQ("Manufacturer""Unknown")
);
planeToUpdate.Manufacturer = "Panavia Aircraft GmbH";
planesCollection.Save(planeToUpdate);
Note that you can also perform a partial update to a document (not as a block), refer to the Update method on the MongoCollection class.

Finally, to remove a set of documents based on a query from the planes collection - in this case we'll remove all planes manufactured by Lockheed from the collection:
planesCollection.Remove(
    Query.EQ("Manufacturer""Lockheed Skunk Works")
);

Friday, 9 November 2012

Initial thoughts on NoSQL with MongoDB

I have heard about the NoSQL movement for a while now but only recently started to properly look into a NoSQL technology. A few weeks ago I found out that 10gen (the team behind the popular NoSQL database called MongoDB) are starting a free "MongoDB for developers" course. The course runs weekly, with each week containing a number of videos that you watch, take a quiz on and then submit homework for. So far it has been very useful despite some initial hiccups in administrating the course. We're now starting Week 3 and have already covered quite a lot about NoSQL in general and how MongoDB can be used in a CRUD sense.
 
Week 3 of the course dives deeper into schema design in MongoDB and compares this with schema design in the more familiar relational data model. The course also has a project that runs throughout - which is to create a simple blogging engine using MongoDB and Python. It seems like the future weekly homeworks are going to be geared towards extending the blogging engines' functionality. Although the course material is using Python to communicate with MongoDB, there are a number of driver libraries that you can download from the MongoDB website for other programming languages - such as C#. I haven't had a chance to play with the C# driver yet but plan on writing a blog post about my experience with it in the future.
 
My training at university and all of the work experience I have gained so far has been around working with relational database management systems. So it is quite exciting moving away from the relational mindset and thinking about a different way to persist application data. It is weirdly refreshing to work with JSON documents in collections than working with the tables and rows abstraction in relational databases.
 
One thing that the course instructors emphasise about MongoDB is that your schema design should be geared towards how your application will access your data. So unlike in a relational data model, where you typically normalise your model to the third normal form - in MongoDB your data model will reflect the access pattern your application will use to get or update the data. As MongoDB doesn't provide support for joining data, you typically model your data (or JSON documents) so that each document contains all of the necessary information your application will regularly need for a particular use case. This is interesting as I think there is an implicit assumption here that you'd only ever have one particular access pattern for your MongoDB database... for example what if I wanted to build another application that accesses the data for a purpose that is different from when the original MongoDB database was designed? I'm hoping the answer to this question is not along the lines of migrating/copying the data to a schema that's more appropriate for the new application!
 
At this stage of my exploration, another concern with MongoDB is that there doesn't seem to be a quick out-of-the-box method to generate a pretty looking report for the data stored in the collections. In the relational world, SQL with the table and rows abstraction lends itself nicely to generating reports that non-technical users can easily make sense of. With MondoDB, you can query your collections of JSON documents easily enough in the shell using mongo.exe and the find() method on a collection - but the resulting output is in JSON format which probably won't go down too well with a non-technical user. Having said this, there may still be solutions/explanations to these concerns that I haven't come across - so I'm definitely not letting them stop me from diving deeper into the NoSQL MongoDB world.