Published: January 28 2015

Unit of Work + Repository Pattern in MVC5 and Web API 2 with Fluent NHibernate and Ninject

After a lot of digging around to figure out the best way to implement the Unit of Work + Repository pattern in an ASP.NET MVC 5 or Web API 2 application, I came up with this solution.

It was used for a custom CMS application that was part of a web development project in Sydney for a law firm marketing website.

My main goal was to keep the code simple and practical without too much over-engineering. To show my approach I've created a sample MVC5 application that displays a list of products, the result isn't very exciting but it allows you to see everything working end to end.

The sample solution is a typical 3 tiered architecture with a separate project for the Domain, Data and Web tiers and is configured to use SQL Server. To keep things (relatively) short I’m only going to talk about the pieces that relate to the Unit of Work & Repository patterns, I’m assuming you’re already familiar with MVC5, Fluent NHibernate and Ninject.

Code available at https://github.com/cornflourblue/mvc5-unit-of-work-example

Update 21 May 2016:

  • Added Web API 2 example project that implements Unit of Work and Repository pattern
  • Added Rollback method to UoW class to enable rolling back transactions for caught exceptions. 
  • Replaced Session.Close() with Session.Dispose() based on best practice recommendations found online. 
  • Wrapped transaction commit and rollback calls in if statements to ensure transaction is active.


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, committing and rolling back a transaction.

public interface IUnitOfWork
{
    void BeginTransaction();
    void Commit();
    void Rollback();
}


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, committing and rolling back 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
        {
            // commit transaction if there is one active
            if (_transaction != null && _transaction.IsActive)
                _transaction.Commit();
        }
        catch 
        {
            // rollback if there was an exception
            if (_transaction != null && _transaction.IsActive)
                _transaction.Rollback();

            throw;
        }
        finally
        {
            Session.Dispose();
        }
    }

    public void Rollback()
    {
        try
        {
            if (_transaction != null && _transaction.IsActive)
                _transaction.Rollback();
        }
        finally
        {
            Session.Dispose();
        }
    }
}


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));
    }
}


Unit of Work in ASP.NET MVC 5

When implementing the Unit of Work and Repository pattern in MVC 5, 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();
    }
}

 

Unit of Work in Web API 2

When implementing the Unit of Work and Repository pattern in Web API 2, the web api layer is the responsible for the configuration of dependency injection and transaction management.

NinjectWebCommon Class

Unlike the MVC 5 implementation, the NinjectWebCommon class must be added manually when used with the Ninject.Web.WebApi NuGet package, and it depends on a custom Ninject based dependency resolver.

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
{
    private static readonly Bootstrapper bootstrapper = new Bootstrapper();

    /// <summary>
    /// Starts the application
    /// </summary>
    public static void Start()
    {
        DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
        DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
        bootstrapper.Initialize(CreateKernel);
    }

    /// <summary>
    /// Stops the application.
    /// </summary>
    public static void Stop()
    {
        bootstrapper.ShutDown();
    }

    /// <summary>
    /// Creates the kernel that will manage your application.
    /// </summary>
    /// <returns>The created kernel.</returns>
    private static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
        kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

        RegisterServices(kernel);

        // Install our Ninject-based IDependencyResolver into the Web API config
        GlobalConfiguration.Configuration.DependencyResolver = new NinjectResolver(kernel);

        return kernel;
    }

    /// <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());
    }
}


Web API UnitOfWorkActionFilter Class

The unit of work action filter 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.

public class UnitOfWorkActionFilter : ActionFilterAttribute
{
    public IUnitOfWork UnitOfWork { get; set; }

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        UnitOfWork = actionContext.Request.GetDependencyScope().GetService(typeof(IUnitOfWork)) as IUnitOfWork;
        UnitOfWork.BeginTransaction();
    }

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        UnitOfWork = actionExecutedContext.Request.GetDependencyScope().GetService(typeof(IUnitOfWork)) as IUnitOfWork;
        if (actionExecutedContext.Exception == null)
        {
            // commit if no exceptions
            UnitOfWork.Commit();
        }
        else
        {
            // rollback if exception
            UnitOfWork.Rollback();
        }
    }
}

Add UoW Action Filter to Global Filters

The UnitOfWorkActionFilter is added globally in the WebApiConfig class.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        ...

        // add UoW action filter globally
        config.Filters.Add(new UnitOfWorkActionFilter());
    }
}
 


Need Some ASP.NET Help?

Search fiverr for freelance ASP.NET developers.


Follow me for updates

On Twitter or RSS.


When I'm not coding...

Me and Tina are on a motorcycle adventure around Australia.
Come along for the ride!


Comments


Supported by