Click here to Skip to main content
Click here to Skip to main content

MongoDB GenericDAO with LINQ support

By , 9 Nov 2012
 

Introduction

This article describes how to implement an generic DAO pattern using the MongoDB C# driver and how to use it in a simple ASP.NET MVC 3 application.

MongoDBEntity 

To represent all documents in MongoDB we can use any class that inherits from an abstract MongoDB Entity. 

using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson;

namespace MongoDbGenericDao
{
    public abstract class MongoDBEntity
    {
        [BsonRepresentation(BsonType.ObjectId)]
        public string Id { get; set; }
    }
}

MongoDBEntity is marked as abstract so it can be used only through specific MongoDBEntity DAO implementations.

[BsonRepresentation(BsonType.ObjectId)] defines what property will be used to be an "_id" identifier in a MongoDB document serialization.

Classes

The Author class will be used by GenericDao.

namespace Modal
{
    public class Author : MongoDBEntity
    {
        public string Name { get; set; }
        public string Email { get; set; }
    }
}

The Generic DAO interface

using System;
using System.Collections.Generic;

namespace MongoDbGenericDao.Interfaces
{
    public interface IDao<T, ID> where T : MongoDBEntity
    {
        T Save(T pobject);
        T GetByID(ID id);
        T GetByCondition(System.Linq.Expressions.Expression<Func<T, bool>> condition);
        IEnumerable<T> GetAll();
        IEnumerable<T> GetAll(System.Linq.Expressions.Expression<Func<T, bool>> condition);
        IEnumerable<T> GetAll(System.Linq.Expressions.Expression<Func<T, bool>> condition, int maxresult, bool orderByDescending);
        IEnumerable<T> Paginate(System.Linq.Expressions.Expression<Func<T, bool>> func, int pagesize, int page, bool pOrderByDescending);
        void Delete(T pobject);
        long Count(System.Linq.Expressions.Expression<Func<T, bool>> condition);
    }
}

On IDao<T, ID>:

  1. T represents a MongoDBEntity class
  2. ID represents the MongoDB "_id" identifier type, string by default

The Generic DAO implementation

using System;
using System.Collections.Generic;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using MongoDB.Driver.Builders;
using MongoDB.Bson;
using System.Linq;

namespace MongoDbGenericDao
{
    public class MongoDBGenericDao<T> : Interfaces.IDao<T, string> where T : MongoDBEntity
    {
        private MongoDatabase _repository;
        private readonly string collectioname = typeof(T).Name;

        public MongoDBGenericDao(string pConnectionstring)
        {
            var conn = new MongoConnectionStringBuilder(pConnectionstring);
            _repository = MongoServer.Create(conn).GetDatabase(conn.DatabaseName);
        }

        public T GetByID(string _id)
        {
            return _repository.GetCollection<T>(collectioname).FindOne(Query.EQ("_id", new ObjectId(_id)));
        }

        public IEnumerable<T> GetAll()
        {
            return _repository.GetCollection<T>(collectioname).FindAll();
        }

        public T GetByCondition(System.Linq.Expressions.Expression<Func<T, bool>> condition)
        {
            return _repository.GetCollection<T>(collectioname).AsQueryable().Where(condition).FirstOrDefault();
        }

        public IEnumerable<T> GetAll(System.Linq.Expressions.Expression<Func<T, bool>> condition)
        {
            return _repository.GetCollection<T>(collectioname).AsQueryable().Where(condition).ToList();
        }

        public IEnumerable<T> GetAll(System.Linq.Expressions.Expression<Func<T, bool>> condition, 
               int maxresult, bool orderByDescending = false)
        {
            var query = _repository.GetCollection<T>(collectioname).AsQueryable().Where(condition);

            if (orderByDescending)
                query.OrderByDescending(x => x.Id);
            else
                query.OrderBy(x => x.Id);

            return query.Take(maxresult);
        }

        public T Save(T pobject)
        {
            _repository.GetCollection<T>(collectioname).Save(pobject);
            return pobject;
        }

        public void Delete(T pobject)
        {
            _repository.GetCollection<T>(collectioname).Remove(Query.EQ("_id", new ObjectId(pobject.Id)));
        }

        public long Count(System.Linq.Expressions.Expression<Func<T, bool>> condition)
        {
            return _repository.GetCollection<T>(collectioname).AsQueryable().LongCount();
        }

        public IEnumerable<T> Paginate(System.Linq.Expressions.Expression<Func<T, bool>> func, 
               int pagesize, int page, bool pOrderByDescending = false)
        {
            var query = _repository.GetCollection<T>(collectioname).AsQueryable().Where(func);

            if (pOrderByDescending)
                query.OrderByDescending(x => x.Id);
            else
                query.OrderBy(x => x.Id);

            return query.Skip(pagesize * (page - 1)).Take(pagesize);
        }
    }
}

This implementation offers all CRUD methods with LINQ support and a useful Paginate method.

LINQ support is native on C# driver.

Business implementation

To use the code just inherit the class GenericDao<t,> and specify the IDao<t,> interface. 

using MeuCondominio.Business.Interfaces;
using MeuCondominio.Modal.Concrete;
using MongoDbGenericDao;

namespace MeuCondominio.Business.Business
{
    public class BAuthor : MongoDBGenericDao<Author>, IDao<Author,string>
    {
        public BAuthor(string connectionstring)
            : base(connectionstring)
        {

        }
    }
}

Usage

Create a new ASP.NET MVC 3 application and add the MongoDB connectionstring on the web.config file.

<connectionstrings>
    <clear />
    <add name="MongoServerSettings" connectionstring="server=localhost;database=test"> </add>
</connectionstrings>

On the controllers, just create a business variable from GenericDAO and be happy. 

// GET: /Home/
public ActionResult Index()
{
    var _BAuthor = new MongoDbGenericDao.MongoDBGenericDao<Author>(
        WebConfigurationManager.ConnectionStrings["MongoServerSettings"].ConnectionString);

    //get 10 authors
    return View(_BAuthor.GetAll().Take(10).ToList());
}	

[HttpDelete]
public ActionResult Remove(string Id)
{
    var _BAuthor = new MongoDbGenericDao.MongoDBGenericDao<Author>(
      WebConfigurationManager.ConnectionStrings["MongoServerSettings"].ConnectionString);

    //get author by Id
    var author = _BAuthor.GetByID(Id);

    //remove an author
    _BAuthor.Delete(author);

    return RedirectToAction("Index");
}

[HttpPost]
public ActionResult AddAuthor(string name, string email)
{
    Author author = new Author
    {
        Name = name,
        Email = email
    };

    var _BAuthor = new MongoDbGenericDao.MongoDBGenericDao<Postagem>(
      WebConfigurationManager.ConnectionStrings["MongoServerSettings"].ConnectionString);

    //Adds the author object and redirects to "Index" action
    _BAuthor.Save(author);

    return RedirectToAction("Index");
}

Points of Interest

A good practice is to inject the business class using a dependency injection framework like Ninject or StructureMap.

You can use my MongoDbGenericDAO project on github, I'll be very excited to improve the project. 

License

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

About the Author

Lazaro Lima
Architect
Brazil Brazil
Lazaro is a brazilian software architect, live in São Paulo, focusing on web development, NoSql and agile practices for startups.

In his personal life he is a part time runner, speaker, amateur astronomer, gamer and an absorbing knowledge ambulant.
Follow on   Twitter   Google+

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionHow to connect MongoDB database using ASP.NET?memberMember 917112829-Apr-13 1:29 
QuestionDont use inheritancememberRafael Nicoletti4-Feb-13 11:18 

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

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130619.1 | Last Updated 9 Nov 2012
Article Copyright 2012 by Lazaro Lima
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid