Using NHibernate in ASP.NET MVC

This is a continuation of a “truck tracker” sample application that leverages NHibernate as an object relational mapper (ORM). I previously created the entities, defined a repository interface, and implemented the repository using NHibernate. During this blog post, I will be exploring how to use this repository in an ASP.NET MVC application.

image Let’s quickly look at what we have so far. To the left is a screenshot of the solution. The following  projects are visible:

  • DTOS – A project to contain the entity (or data transfer objects). These are lean and clean. These are simple classes that only contain properties for the objects. The classes contain no business logic. This project does not reference any NHibernate libraries.

RepoInfrastructure – This project contains only infrastructure objects. I like to keep this clean of any application specific objects. These objects are pure and reusable in any application. Here you see mostly interface definitions for the repository. I keep this type of objects checked into SVN as on its own and pull it into applications using SVN external.

NHibernateLayer – This project contains all the NHibernate specific objects. In the ‘Maps’ folder are the mappings for the DTOS to the relational database. There is also an implementations of the repository for our application specific domain.

TruckTrackerWeb – This is the ASP.NET MVC project where we want to use the NHibernate repository.

The following are a few architecture / design considerations that I will be trying to balance. These decisions are rarely black or white. Usually, it is about compromise. Sometimes we make the application simpler, but the code is less efficient. Other times we add complexity to factor out code to make it more maintainable.

  • We have done a decent job of separating the concerns (SOC) of the various layers. The DTOS for instance know nothing about the database or NHibernate. Only the NHibernateLayer knows about NHibernate. The design should continue to consider SOC as this is important for maintainability and testability.
  • NHibernate has two abstractions: ISessionFactory and ISession.
    • Instances of ISessionFactory are expensive to create. Generally, you try to create this instance and use it for the application lifetime. Previously, we encapsulated the creation of ISessionFactory into the ‘NHibernateHelper’ object. As you can imagine, an instance of ISessionFactory is used to create ISession instances.
    • Instances of ISession are meant to be used for a single transaction. The lifetime of the ISession instance determines the ‘unit of work’. Previously, we encapsulated the ISession instance into a ‘UnitOfWork’ object. Generally, the idea is to make the unit of work as small as possible. In other words, the life time of the ISession objects should be as small as possible.

In our ASP.NET MVC application we will create an instance of ISessionFactory and use it for the life of our application. The lifetime that makes the most sense for the ISession instance is a ‘per request’ basis. A bit of care is needed here in that if we are using NHibernate’s lazy loading, we may still want the ISession available to the view. In the following, we will discuss the pros / cons of the more common ways to wire up our NHibernate infrastructure.

Method 1 – Do It Your Self

The first method of wiring up NHibernate will extend the ‘HttpApplication’ instance. You can read more about this method on Ayende’s blog. Here is the code to wire it up:

public class MvcApplication : System.Web.HttpApplication
{
    /* For demo purposes only...put this connection string in the web.config. */
    private const string _connectionString = @"Server=localhost; Port=3306; Database=trucktracker; Uid=root; Pwd='your_own_password';";
    private static readonly ISessionFactory _sessionFactory = CreateSessionFactory();

    protected static ISessionFactory CreateSessionFactory()
    {
        NHibernateHelper helper = new NHibernateHelper(_connectionString);
        return helper.SessionFactory;
    }

    public static IUnitOfWork UnitOfWork
    {
        get { return (IUnitOfWork)HttpContext.Current.Items["current.uow"]; }
        set { HttpContext.Current.Items["current.uow"] = value; }
    }

    public static IIntKeyedRepository<Truck> CreateTruckRepo()
    {
        return new Repository<Truck>(((UnitOfWork)UnitOfWork).Session);
    }

    protected MvcApplication()
    {
        // This configures the unit or work to be on a per request basis.
        //
        BeginRequest += delegate
                            {
                                UnitOfWork = new UnitOfWork(_sessionFactory);
                            };
        EndRequest += delegate
                          {
                            if (UnitOfWork != null)
                              {
                                // Notice that we are rolling back unless
                                //    an explicit call to commit was made elsewhere.
                                //
                                UnitOfWork.Dispose();
                              }
                          };
    }

     /* Removed 'RegisterRoutes' and 'Application_Start' for brevity' */
}

This method creates a static instance of ‘ISessionFactory’ that has the lifetime of the web application. During instantiation, the constructor creates and disposes of a ‘UnitOfWork’ instance by hooking into the ‘BeginRequest’ and ‘EndRequest’ events. This creates a ‘UnitOfWork’ (or ‘ISession’) instance that has the lifetime of the web request. Notice the default behavior of the ‘UnitOfWork’ is to rollback (or not commit) the transaction. Committing changes must be done explicitly.

One static property ‘UnitOfWork’ and one static method ‘CreateTruckRepo’ expose generic (not NHibernate specific) interfaces to the rest of the application. This prevents the proliferation of coupling to the NHibernate library and isolates the code that needs to be changed if we switch to another ORM. The ‘UnitOfWork’ property tucks away the ‘IUnitOfWork’ instance into the ‘HttpContext.Current.Items’ dictionary. The ‘CreateTruckRepo’ is a factory method that to create truck repositories.

public class HomeController : Controller
{
    private readonly IIntKeyedRepository<Truck> _truckRepository;

    public HomeController()
    {
        _truckRepository = MvcApplication.CreateTruckRepo();
    }

    public ActionResult Trucks()
    {
        return View(_truckRepository.All());
    }
}

The code above is an example of using this method in an ASP.NET MVC controller. A generic repository instance for ‘Truck’ is created by accessing the static ‘CreateTruckRepo’ factory method on the application. Notice, that the view is passed the IQueryable result of ‘All’. This will leverage NHibernate’s lazy-loading features and thus the ISession instance must remain alive into the view (which it does).

It is worth re-iterating how the NHibernate coupling has been controlled. The Global.asax.cs file is the only file that contains any NHibernate specific code. The controllers and views will only use the generic ‘IUnitOfWork’ and ‘IIntKeyedRepository<Truck>’ interfaces.

In this example, there is not much need to refactor the NHibernate specific code into an object to remove it from the global.asax.cs file. If we had many repositories, multiple databases, or more complex session lifetime then it may be justifiable to refactor.

Method 2 – Using Ninject (or another IOC container)

The above method works great for simple applications. If the application is a bit too complex to use the method 1 (or if your application already is using a dependency injection framework) then this method will work better. The following code leverage the Ninject IOC container as the dependency inject framework. I’ve previously posted about getting started with Ninject and will assume that as base knowledge.

Since this application did not have Ninject we first take a couple of simple steps to add it.  First reference the required Ninject DLLs (Ninject.DLL and Ninject.Web.Mvc) in the project. Then modify the global.asax file to look like the following:

<%@ Application Inherits="MvcApplication1.NinjectFramework.NinjectWebsiteApplication" Language="C#" %>

Then deleted the ‘global.asax.cs’ file. The above line now makes the application inherit from the ‘NinjectWebsiteApplication’ type instead of the ‘System.Web.HttpApplication’ type. Here is the implementation of this new application type:

public class NinjectWebsiteApplication : NinjectHttpApplication
{
    protected override void OnApplicationStarted()
    {
        AreaRegistration.RegisterAllAreas();

        RegisterAllControllersIn(Assembly.GetExecutingAssembly());

        var bootstrapper = Kernel.Get<Bootstrapper>();

        bootstrapper.RegisterRoutes();
    }


    protected override IKernel CreateKernel()
    {
        var kernel = new StandardKernel();

        kernel.Load<RepositoryModule>();

        return kernel;
    }
}

The base type of this object is ‘NinjectHttpApplication’ which is part of the Ninject library and inherits from the original application type of ‘System.Web.HttpApplication’. Recall that ASP.NET MVC controllers are created by a controller factory that is supplied by the ‘out of the box’ ASP.NET MVC library. However, we don’t want ASP.NET to create our controllers, we want Ninject to do this for us. So the ‘NinjectHttpApplication’ provides convenience methods that register all ASP.NET MVC controllers with a Ninject provided controller factory.

The above code does some additional boot strapping to get the application started. Most of this is now done in a class called ‘Bootstrapper’ and is shown below:

public class Bootstrapper
{
    public HttpContextBase HttpContext { get; private set; }
    public RouteCollection Routes { get; private set; }

    public Bootstrapper(HttpContextBase httpContext, RouteCollection routes)
    {
        HttpContext = httpContext;
        Routes = routes;
    }

    public void RegisterRoutes()
    {
        Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        Routes.MapRoute("Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = "" });
    }
}

This is simply registering the routes for the ASP.NET MVC routing engine.

The ‘NinjectHttpApplication’ type has an abstract ‘CreateKernel’ method which you must implement. Here we can register any number of ‘Ninject modules’ to wire up the application. The following is the ‘RepositoryModule’ that is used to wire up the NHibernate repository for this example:

public class RepositoryModule : NinjectModule
{
    public override void Load()
    {
        /* For demo only...this should be in the web.config. */
        const string connectionString = @"Server=localhost; Port=3306; Database=trucktracker; Uid=root; Pwd='your_own_password';";

        NHibernateHelper helper = new NHibernateHelper(connectionString);
        Bind<ISessionFactory>().ToConstant(helper.SessionFactory)
            .InSingletonScope();

        Bind<IUnitOfWork>().To<UnitOfWork>()
            .InRequestScope();
        Bind<ISession>().ToProvider(new SessionProvider())
            .InRequestScope();
        Bind<IIntKeyedRepository<Truck>>().To<Repository<Truck>>()
            .InRequestScope();
    }
}

public class SessionProvider : Provider<ISession>
{
    protected override ISession CreateInstance(IContext context)
    {
        UnitOfWork unitOfWork = (UnitOfWork)context.Kernel.Get<IUnitOfWork>();
        return unitOfWork.Session;
    }
}

We bind the ‘ISessionFactory’ interface to a constant returned by the ‘SessionFactory’ property of the NHibernate helper. This is bound in a ‘InSingletonScope’ lifetime. In other words, we will only ever get one of these per application.

Next we bind ‘IUnitOfWork’, ‘ISession’ and ‘IIntKeyedRepository’ interfaces to their implementations. All of these are bound to a ‘InRequestScope’ lifetime which means one instance per web request. The only tricky one is the ‘ISession’ binding where we had to use a Ninject provider because the session we want is encapsulated in the unit of work instance.

Our controller has been modified to the following code:

public class HomeController : Controller
{
    private readonly IIntKeyedRepository<Truck> _truckRepository;

    public HomeController(IIntKeyedRepository<Truck> truckRepository)
    {
        _truckRepository = truckRepository;
    }

    public ActionResult Trucks()
    {
        return View(_truckRepository.All());
    }
}

Notice, there is no default constructor. This is only required by the ‘out of the box’ ASP.NET MVC controller factory. Ninject will now manage the lifetimes of the constructor injected objects.

This method of wiring up NHibernate is a bit more powerful because we are using an IOC container to handle the dependency injection and manage the object lifetimes.

Summary

Wiring up the previously created NHibernate repository into an ASP.NET MVC application is easy to do. Either of the above methods will work. It is extremely important to the maintainability of the application that the NHibernate coupling be localized. In method 1 we localized that logic into the global.ascx.cs file and in method 2 into the ‘RepositoryModule’ file. In both cases, the controllers, models, and views use only generic (NHibernate agnostic) interfaces.

Comments
  1. hung
  2. Bob Cravens
  3. JDH
    • rcravens
  4. Nelson

Leave a Reply

Your email address will not be published. Required fields are marked *

*