Click here to Skip to main content
11,710,472 members (81,801 online)
Click here to Skip to main content

Tagged as

Using MongoDb With Web API 2

, 28 Jun 2014 CPOL 11K 16
Rate this:
Please Sign up or sign in to vote.
Using MongoDb With Web API 2

Introduction

I was writing a quick how-to to a couple of colleagues to get them up and running with MongoDb and I decided to share it with you.

I am showing how I built a small application to record some specific activities in my software and removed some complexity to keep it simple and easy to read, but it does not change anything whether your POCOs have 2 or 20 properties.

Basically, it's a step-by-step. So, go!

  • Go to MongoDb web site: http://www.mongodb.org/ and download the MongoDb zip that fits with your requirements. In this case, I downloaded Windows 64 bit.
  • Unzip the file and you will get a couple of folders.
  • Add data folder beside the bin folder.
  • Select bin folder, hit shift key and right click to get to command window.
  • Type: mongod --dbpath ../data

The last step is to indicate to Mongo the path to data where the documents will be stored.

Once those steps are done, add a new Web API project in Visual Studio.

  • Add Domain folder for the domain model.

Note that here usually I use a library project. But for this quick start, I will just set up everything in one project.

Back to the solution:

  • Add a Repository folder inside the Domain. This one will be for repositories contracts (or interfaces if you want).
  • Add services folder to serve the controllers.
  • Add repositories folder for repositories implementations.
  • Add an empty web API controller.

Again, here I will not use any IoC for Dependency Injection. So I will new up any instance I need.

At this point, your solution is set up and MongoDb up. So let's start by Repository pattern and add the following IRepository inside domain/repositories:

namespace m2a.Domain.Repositories
{
   public interface IRepository<T>
    {
       T Add(T entity);
       IEnumerable<T> GetAll();
    }
}

This general repository, most of the time, will carry all CRUD operations unless you have any specific needs.

Then comes ILogActivityRepository and here you can add any specific function to log activity. In my case, nothing special.

namespace m2a.Domain.Repositories
{
    public interface ILogActivityRepository:IRepository<LogActivity>
    {
    }
}

Then, add a domain class and as you can see my Domain objects are POCOs and they express my Domain Model and nothing else. For any Business Rules validation, I use Specification pattern to do it cleanly. But it's another topic and you can read my article about it here.

namespace m2a.Domain
{
    public class LogActivity
    {
        public string ActivityName{ get; set; }
        public string Message{ get; set; }        
    }
}

And then in the repository folder, add the implementation of the repository:

namespace m2a.Repositories
{
    public class LogActivityReppository:ILogActivityRepository
    {
        private MongoCollection _logActivitiesCollection;
        public LogActivityReppository(MongoDatabase logActivity)
        {
            if (logActivity == null) throw new NullReferenceException("Mongodatabase cannot be null. Please check your settings!");
            _logActivitiesCollection = logActivity.GetCollection<LogActivity>("activities");
        }

        public LogActivity Add(LogActivity la)
        {
            la.Id = ObjectId.GenerateNewId().ToString();
            _logActivitiesCollection.Insert(la);
            return la;
        }

        public IEnumerable<LogActivity> GetAll()
        {
            return _logActivitiesCollection.FindAllAs<LogActivity>();
        }
    }
}

Here comes the LogActivityService which inherits from ServiceBase. The namespaces in the following snippets will indicate where these objects should go in the Visual Studio solution.

namespace m2a.Services
{
    public class LogActivityService : ServiceBase
    {
        private LogActivityRepository _repo;
        public LogActivityService()
        {
            _repo = new LogActivityRepository(GetDB());
        }

        internal IEnumerable<LogActivity> GetAll()
        {
            return _repo.GetAll();
        }

        internal void Add(LogActivity la)
        {
            _repo.Add(la); ;
        }       
    }
}

namespace m2a.Services
{
    public  class ServiceBase
    {
        public MongoDatabase GetDB()
        {
            var db = new MongoConfig();
            return db.Database;
        }
    }
}

Here, notice that the DI is injected via LogActivityReppository constructor. This means my Service layer is bound to Mongo which is not a good practice.

If you have any IoC to take care of those dependencies, it's ok. But if you don't move the MongoDb stuff to the repository. It belongs there. So, it's pretty easy to rename ServiceBase to RepositoryBase and move it to Repositories folder and you will be done!

Now, let's add a controller to expose Get and Post methods to be able to insert a document to MongoDb and get it back:

namespace m2a.Controllers
{
    public class LogActivitiesController : ApiController
    {
        private LogActivityService _service;
        public LogActivitiesController()
        {
            _service = new LogActivityService();
        }
        // GET api/logactivities
        public IEnumerable<LogActivity> Get()
        {
            var res = _service.GetAll();
            return res;
        }        

        // POST api/logactivities
        public LogActivity Post([FromBody]LogActivity la)
        {
           return _service.Add(la);
        }       
    }
}

Note that the [FromBody] in the Post is not required when you pass an object to your method because the objects are by default in the body of the HTTP request.

It's required for the primitive because they are in the header by default and that's why we don’t specify [FromUri]. Again, all these conventions go under the MVC mantra: convention over configuration.

And finally, instead of using Fiddler to add an activity, I write two Unit Tests to add an activity to MongoDb and another one to get it back to make sure the activity is stored and now is a document.

Note that this test is just for adding a value and not intended to be a unit test because I hit the database (so it's an integration test) and I recommend to use a mocking framework for your unit tests to avoid any slowing or adding any unwanted data to database.

[TestMethod]
 public void should_succeed_if_logactivity_id_is_set_to_Mongodb_objectId()
 {
     var lac = new LogActivitiesController();
     var la = new LogActivity
     {
         ApplicationName = "m2a website will be updated one day!",
         ApplicationId = "2",
         ApplicationPath = "m2a.ca",
         ApplicationServer = "",
         ClassName = "yeah",
         Id = "",
         LogDate = DateTime.Now,
         UserId = 22,
         Username = "m2a",
         BusinessFunctionName="adding a quote",
         Parameters="name, id, date, others"

     };
    var res=  lac.Post(la);
    Assert.IsTrue(res.Id != "");
 }

 [TestMethod]
 public void should_succeed_if_there_is_any_activity()
 {
    var lac = new LogActivitiesController();
    var res= lac.Get();
    Assert.IsNotNull(res);
 }

And that's it! You have a MongoDb database and Web API 2 to add or get an activity.

Of course, this is just a beginning but you can define any complex objects graph and your data will be serialized and stored as Mongo document. By the way, in MongoDb, everything is a document and I encourage you to read more about it here: http://www.mongodb.org/

Now, if you look at my serviceBase class, you notice that I instantiate a MongoConfig which creates a mongo database object as follows:

public class MongoConfig
{
      public MongoDatabase Database;

      public MongoConfig()
      {
          var client = new MongoClient(Settings.Default.m2aConnectionString);
          var server = client.GetServer();
          Database = server.GetDatabase(Settings.Default.m2aDatabaseName);
      }
  }

And maybe you are saying, but what about the performance if you have to instantiate this configuration every time a service is called?

Yeah! You right! I thought about it a lot because I was thinking that this configuration should be as a NHibernate session: a cost operation that should be done once on the application start up. RavenDb, another document database works also as NHibernate and that’s why (as many people I worked with) I was confused.

But it's not the case for MongoDb because the Mongocsharpdriver is doing the magic behind the scene. It caches all the objects and that's why you don't need to worry about it.

SQL vs NoSQL

Why a .NET developer should use a NoSQL database when you get SQL Server database at hand and many its variants as the local one you can embed in your project (localdb) or the compact version?

The most important and first reason in my opinion is impedance mismatch. I will not dive into this but I can give you an example to illustrate my point.

I have client object with an Address and List of Orders. The Order itself contains a list of products as follow:

public class Client
  {
        public string Id { get; set; }
        public string Name { get; set; }
        public Address Address { get; set; }
        public IEnumerable<Order> Orders { get; set; }
    }

    public class Address
    {
        public string Id { get; set; }
        public string Street { get; set; }
        public string City { get; set; }
    }
    public class Order
    {
        public string Id { get; set; }
        public DateTime DateTime { get; set; }
        public IEnumerable<Product> Products { get; set; }
    }

    public class Product
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public double Price { get; set; }
    }

Here, in the relational database, the client data will be stored in 3 tables: client, address and Order. The Order data will be stored also in minimum two tables. You’ve got three entities in the databases that carry the data you need for one client object. You need here some sort of factory to map the data and be able to get it access to it via your objects. This difference is the impedance mismatch.

The second reason lays in the fact that document database are most of the time in JSON format (MongoDb is in Bson format) and this means they are not bound to any language or technology to be extracted or manipulated. That’s why some folks are using JavaScript libraries to build extraordinary applications.

Hope this gave you a starting point for exploring more MongoDb. There are also few nice client applications that give you access to the MongoDb documents. I tried MongoVue and it’s free and pretty cool.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Mack Ait-Aoudia
Software Developer (Senior) http://www.m2a.ca
Canada Canada
No Biography provided

You may also be interested in...

Comments and Discussions

 
GeneralMy vote of 5 Pin
razkp00713-Apr-15 0:49
memberrazkp00713-Apr-15 0:49 
QuestionID's Pin
Member 1145084513-Feb-15 9:26
memberMember 1145084513-Feb-15 9:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150819.1 | Last Updated 28 Jun 2014
Article Copyright 2014 by Mack Ait-Aoudia
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid