Pages

Unit of Work with Repository Pattern in MVC5 with Fluent NHibernate and Ninject

Unit of Work Pattern

The Unit of Work pattern is used to group one or more operations (usually database operations) into a single transaction or “unit of work”, so that all operations either pass or fail as one.

Repository Pattern

The Repository pattern is used to manage CRUD operations through an abstract interface that exposes domain entities and hides the implementation details of database access code.

Implementation

Domain Layer

The domain/business logic layer contains the interfaces for a unit of work and a generic repository.

IUnitOfWork Interface

A lightweight Unit of Work interface that defines methods for beginning and committing a transaction.
public interface IUnitOfWork
{
    void BeginTransaction();
    void Commit();
}

IRepository < t > Interface

A generic repository interface that exposes a standard set of methods for performing CRUD operations on entities within the system.
public interface IRepository< t >where T : IEntity
{
    IQueryable<t>
        GetAll();
        T GetById(int id);
        void Create(T entity);
        void Update(T entity);
        void Delete(int id);
}

Data Layer

The data layer contains the implementations of the above unit of work and repository interfaces using Fluent NHibernate as the ORM.

UnitOfWork Class

The UnitOfWork class contains methods for beginning and committing a transaction, it also exposes a Session property that returns the current NHibernate Session associated with the unit of work. Each UnitOfWork instance contains a single session.
The static constructor is used to implement the Singleton pattern for the NHibernate session factory, in C# static constructors are executed only once per application domain and are thread-safe which makes them ideal for implementing singletons.
public class UnitOfWork : IUnitOfWork
{
    private static readonly ISessionFactory _sessionFactory;
    private ITransaction _transaction;
 
    public ISession Session { get; private set; }
 
    static UnitOfWork()
    {
        // Initialise singleton instance of ISessionFactory, static constructors are only executed once during the
        // application lifetime - the first time the UnitOfWork class is used
        _sessionFactory = Fluently.Configure()
            .Database(MsSqlConfiguration.MsSql2008.ConnectionString(x => x.FromConnectionStringWithKey("UnitOfWorkExample")))
            .Mappings(x => x.AutoMappings.Add(
                AutoMap.AssemblyOf<product>(new AutomappingConfiguration()).UseOverridesFromAssemblyOf<productoverrides>()))
            .ExposeConfiguration(config => new SchemaUpdate(config).Execute(false, true))
            .BuildSessionFactory();
    }
 
    public UnitOfWork()
    {
        Session = _sessionFactory.OpenSession();
    }
 
    public void BeginTransaction()
    {
        _transaction = Session.BeginTransaction();
    }
 
    public void Commit()
    {
        try
        {
            _transaction.Commit();
        }
        catch
        {
            _transaction.Rollback();
            throw;
        }
        finally
        {
            Session.Close();
        }
    }
}

Repository< t > Class

A generic repository class that implements methods for performing CRUD operations on domain entities. You may notice that there aren't any transactions in this class, that's because transactions need to be implemented at a higher level because a transaction may contain several operations across different repositories.
public class Repository<t> : IRepository<t> where T : IEntity
{
    private UnitOfWork _unitOfWork;
    public Repository(IUnitOfWork unitOfWork){
        _unitOfWork = (UnitOfWork)unitOfWork;
    }
 
    protected ISession Session { get { return _unitOfWork.Session; } }
 
    public IQueryable<t> GetAll()
    {
        return Session.Query<t>();
    }
 
    public T GetById(int id)
    {
        return Session.Get<t>(id);
    }
 
    public void Create(T entity)
    {
        Session.Save(entity);
    }
 
    public void Update(T entity)
    {
        Session.Update(entity);
    }
 
    public void Delete(int id)
    {
        Session.Delete(Session.Load<t>(id));
    }
}

Web Layer

The web layer is the responsible for the configuration of dependency injection and transaction management.

NinjectWebCommon Class

This class is automatically added when you install the Ninject.MVC5 package from NuGet and contains the configuration for dependency injection. I've left out the code that I didn't touch.
The unit of work binding sets the scope to "InRequestScope()" which ensures that the same IUnitOfWork instance will be used everywhere within a single request. Having a single Unit or Work per request is necessary for the pattern to function correctly.
public static class NinjectWebCommon
{
     ...
 
    /// <summary>
    /// Load your modules or register your services here!
    /// </summary>
    /// <param name="kernel">The kernel.</param>
    private static void RegisterServices(IKernel kernel)
    {
        // unit of work per request
        kernel.Bind<iunitofwork>().To<unitofwork>().InRequestScope();
 
        // default binding for everything except unit of work
        kernel.Bind(x => x.FromAssembliesMatching("*").SelectAllClasses().Excluding<unitofwork>().BindDefaultInterface());
    }
}

MVC 5 BaseController Class

The base controller is used for beginning and committing transactions, this implementation uses a transaction per action so everything within an action is treated as a single Unit of Work. There is also a check to ensure that transactions are not created for child actions since any child action will already be running within the transaction of it's parent action.
I'm using public property injection for the IUnitOfWork property rather than constructor injection so controllers that inherit from BaseController won't need to call the base constructor passing the dependency.
public class BaseController : Controller
{
    [Inject]
    public IUnitOfWork UnitOfWork { get; set; }
 
    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (!filterContext.IsChildAction)
            UnitOfWork.BeginTransaction();
    }
 
    protected override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        if (!filterContext.IsChildAction)
            UnitOfWork.Commit();
    }
}