ASP.NET WebApi with NHibernate and Unity – Application Architecture

| September 16, 2013 | Reply

Recently I had to propose a sample .NET application which exposed a REST API. One of the requirements was that it had to use NHibernate. I am currently used to developing similar Java applications with Spring and Hibernate so naturally I decided I wanted the familiar architecture with clearly differentiated Controller, Service and DAO layers. To make this work, I also needed an IoC container. The one I chose was Unity because it had a good AOP extension called Interception. With those tools I was able to create a sample easily-manageable application with automatic transaction and NHibernate session handling.

The DAO layer

DAOs are used to persist objects and to query the database. There are a few actions common to all DAOs, namely fetching an object by its primary key, fetching a collection of all objects from a table, saving, updating and deleting an object. This interface defines these actions:

    public interface IGenericDao<ENTITY_TYPE, ID_TYPE>
    {
        ENTITY_TYPE Get(ID_TYPE id);
        void Save(ENTITY_TYPE entity);
        void Update(ENTITY_TYPE entity);
        void Delete(ENTITY_TYPE entity);
        IList<ENTITY_TYPE> GetAll();
    }

This is the default implementation using NHibernate:

    public class GenericHibernateDao<ENTITY_TYPE, ID_TYPE> : IGenericDao<ENTITY_TYPE, ID_TYPE>
    {
        public ISessionFactory SessionFactory { private get; set; }

        protected ISession CurrentSession
        {
            get
            {
                return SessionFactory.GetCurrentSession();
            }
        }

        public virtual ENTITY_TYPE Get(ID_TYPE id)
        {
            return CurrentSession.Get<ENTITY_TYPE>(id);
        }

        public virtual void Save(ENTITY_TYPE entity)
        {
            CurrentSession.Save(entity);
        }

        public virtual void Update(ENTITY_TYPE entity)
        {
            CurrentSession.Update(entity);
        }

        public virtual void Delete(ENTITY_TYPE entity)
        {
            CurrentSession.Delete(entity);
        }

        public virtual IList GetAll()
        {
            return CurrentSession.CreateCriteria(typeof(ENTITY_TYPE))
                        .List<ENTITY_TYPE>();
        }
    }

Now we are able to create specific DAOs for our entities. This is a sample DAO which works with instances of class User:

    public interface IUserDao : IGenericDao<User, int>
    {
    }

    public class UserDao : GenericHibernateDao<User, int>, IUserDao
    {
    }

If more complex queries are needed, new methods can be defined in IUserDao and consequently implemented in UserDao. The basic logic is good enough for the purposes of this demonstration , so we are not adding anything in particular.
This is how the User class looks like:

    public class User
    {
        public virtual int Id { get; set; }
        public virtual string Username { get; set; }
    }

The Service layer

Services define the business logic of an application. They usually contain references to one or more DAOs and use the results of the queries that DAOs produce. Services are also used for transaction demarcation. This is a sample CRUD service:

    public class UserService
    {
        public IUserDao UserDao { private get; set; }

        [Transactional]
        public virtual IList<User> GetAllUsers()
        {
            return UserDao.GetAll();
        }

        [Transactional]
        public virtual User GetUser(int id)
        {
            return UserDao.Get(id);
        }

        [Transactional]
        public virtual void Save(User user)
        {
            UserDao.Save(user);
        }

        [Transactional]
        public virtual void DeleteUser(User user)
        {
            UserDao.Delete(user);
        }

        [Transactional]
        public virtual void UpdateUser(User user)
        {
            UserDao.Update(user);
        }
    }

All the logic that has to be executed in a single transaction must be contained inside a virtual method, decorated with the attribute Transactional. Later I will explain how I implemented this.

The Controllers

This is how the UsersController looks like:

    public class UsersController : ApiController
    {
        [Dependency]
        public UserService UserService { private get; set; }

        public IEnumerable<User> GetAllUsers()
        {
            return UserService.GetAllUsers();
        }

        public User GetUserById(int id)
        {
            User user = UserService.GetUser(id);
            if (user == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
            return user;
        }

        public HttpResponseMessage PostUser(User user)
        {
            UserService.Save(user);

            HttpResponseMessage response = this.Request.CreateResponse(HttpStatusCode.Created, user);
            return response;
        }

        public HttpResponseMessage PutUser(int id, User user)
        {
            User persistentUser = UserService.GetUser(id);
            if (persistentUser == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
            persistentUser.Username = user.Username;
            UserService.UpdateUser(persistentUser);

            HttpResponseMessage response = this.Request.CreateResponse(HttpStatusCode.Accepted);
            return response;
        }

        public void DeleteUser(int id)
        {
            User user = UserService.GetUser(id);
            if (user == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
            UserService.DeleteUser(user);
        }
    }

Notice that the UserService property is marked with the attribute Dependency. This is a Unity attribute and its purpose will be explained later.

Creating a SessionFactory

This is a sample class which configures and creates a SessionFactory instance. It should be tweaked for your personal needs.

    public static class HibernateConfig
    {
        public static ISessionFactory SessionFactory { get; private set; }

        public static void InitHibernate()
        {
            Configuration cfg = new Configuration();
            ISessionFactory sessionFactory = cfg.Configure().BuildSessionFactory();
            SessionFactory = sessionFactory;
        }
    }

Call HibernateConfig.InitHibernate() from inside Application_Start() in your Global.asax.cs file.

Wiring it all together

Using Unity we can make all of this work together. I recommend using a package called Unity.WebApi which integrates Unity with ASP.NET WebApi applications. It provides code which registers Unity as a DependencyResolver for WebApi Controllers. Here is a bootstrapper class which initialises Unity and registers instances and types with the container.

    public static class UnityBootstrapper
    {
        public static void Initialise()
        {
            IUnityContainer container = BuildUnityContainer();

            GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
        }

        private static IUnityContainer BuildUnityContainer()
        {
            UnityContainer container = new UnityContainer();
            container.AddNewExtension();

            // register all your components with the container here
            // it is NOT necessary to register your controllers

            // e.g. container.RegisterType();    
            RegisterTypes(container);

            return container;
        }

        private static void RegisterTypes(IUnityContainer container)
        {
            container.RegisterInstance<ISessionFactory>(HibernateConfig.SessionFactory);

            container.RegisterType<IUserDao, UserDao>(new ContainerControlledLifetimeManager(), new InjectionProperty("SessionFactory"));

            container.RegisterType<UserService>(new ContainerControlledLifetimeManager(), 
                                                new InjectionProperty("UserDao"),
                                                new InterceptionBehavior(),
                                                new Interceptor());
        }
    }

UnityBootstrapper.Initialise() should also be called from inside Application_Start() but it must be done after the initialisation of NHibernate because we need the configured SessionFactory instance. This is how we inject the SessionFactory reference into the DAOs and the DAO references into the Services. Service references are injected into Controllers using the Dependency attribute thanks to Unity being registered as a DependencyResolver.

Transaction management

Remember the Transactional attribute? It works thanks to Unity Interception.

public class TransactionalAttribute : HandlerAttribute
{
    private IsolationLevel isolationLevel = IsolationLevel.Unspecified;

    public TransactionalAttribute()
    {
    }

    public TransactionalAttribute(IsolationLevel isolationLevel)
    {
        this.isolationLevel = isolationLevel;
    }

    public override ICallHandler CreateHandler(IUnityContainer container)
    {
        return new TransactionalCallHandler
        {
            Order = 1,
            IsolationLevel = isolationLevel,
            SessionFactory = container.Resolve()
        };
    }
}

This is the class that does the actual heavy-lifting:

public class TransactionalCallHandler : ICallHandler
{
    public int Order { get; set; }

    public ISessionFactory SessionFactory { private get; set; }

    public IsolationLevel IsolationLevel { private get; set; }

    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
        ISession session = SessionFactory.GetCurrentSession();

        ITransaction transaction = null;
        if (IsolationLevel == IsolationLevel.Unspecified)
        {
            transaction = session.BeginTransaction();
        }
        else
        {
            transaction = session.BeginTransaction(IsolationLevel);
        }

        IMethodReturn result = getNext()(input, getNext);

        if (result.Exception != null)
        {
            transaction.Rollback();
        }
        else
        {
            transaction.Commit();
        }
        transaction.Dispose();

        return result;
    }
}

In short, Unity scans the objects in the container for attributes which extend HandlerAttribute. Thus Unity can get the actual CallHandler class and apply its logic around the target method. The proposed handler begins a transaction with an optionally defined isolation level, executes the target method and either commits or rolls the transaction back depending on the presence of errors.

Hibernate Session Management

The only thing left to explain is how we manage the Hibernate Sessions. For this type of applications it is considered a good practice to use Session per Request. This can be easily achieved by implementing and registering a global Action Filter which opens a session and binds it to the current HTTP request before a Controller action method gets executed and unbinding, flushing and closing the session after execution.

public class HibernateSessionFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        CurrentSessionContext.Bind(HibernateConfig.SessionFactory.OpenSession());
    }

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        ISession session = CurrentSessionContext.Unbind(HibernateConfig.SessionFactory);
        session.Flush();
        session.Close();
        session.Dispose();
    }
}

The filter can be registered with this line of code:

    GlobalConfiguration.Configuration.Filters.Add(new HibernateSessionFilter());

For the session binding to work, you also need to add this line in your hibernate configuration file:

<property name="current_session_context_class">web</property>

That is it! Now every time an action method is called a Hibernate Session will be opened, Service references will automatically be injected, calls to Transactional methods will automatically manage transacations and so on. Feel free to delve into the attached Visual Studio solution (VS 2012).

Download example project

Tags: , , , , , , ,

Category: Development

About the Author ()

Leave a Reply