65.9K
CodeProject is changing. Read more.
Home

MongoDB GenericDAO with LINQ support

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.86/5 (8 votes)

Nov 8, 2012

CPOL

1 min read

viewsIcon

33205

A C# implementation for GenericDAO pattern for MongoDB with LINQ support.

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.