Use this NHibernate wrapper to keep your repository classes simple

It's no secret that we at Headspring Systems use NHibernate for data access in the custom software systems we deploy.  I, personally, have been using NHibernate since 2005 when version 0.8 was current.  Now, we're approaching the 2.0 version, which I'm very excited about.  With version 2.0, NHibernate will be mostly on par with Hibernate 3.2.

If you are just getting started with NHibernate, some of the first questions you'll need to answer are:

  • How do I configure NHibernate?
  • How do I manage the session factory?
  • When do I create and throw away sessions?

The answers are different based on the context.  If you have a smart client application, you'll need to decide what size you want your units of work (I suggest they be small).  You'll need to create a new instance of ISession for each unit of work and Dispose() of it at the end after commiting the transaction.

It seems the majority of enterprise applications these days are web applications, and since I run a .Net shop, we use ASP.NET.  The unit of work is easy here.  We have one web request be one unit of work.  We create an instance of ISession at the beginning of the web request and dispose of it at the end.

I've created several wrappers for NHibernate over the years, and I think they have improved as my understanding of NHibernate has increased.  Ironically, the wrapper started out much more complex than it is today.  I've simplified it over time, and now, I think I've gotten it down to its simplest essence.

The following is a class called HybridSessionBuilder.  It is appropriate for ASP.NET applications using NHibernate, and you can see it in action in the CodeCampServer codebase.  There is a version that supports multiple databases within the Tarantino project (sometimes you can't have just one).

The key to this is the interface:

using NHibernate;
using NHibernate.Cfg;

namespace CodeCampServer.DataAccess
{
    public interface ISessionBuilder
    {
        ISession GetSession();
        Configuration GetConfiguration();
    }
}

Repository classes should require an instance of ISessionBuilder passed into their constructors, and for each operation, they should call GetSession().  GetConfiguration is there to facilitate SchemaExport, which we use to generate the database schema from the NHibernate mappings.

Below is the HybridSessionBuilder class.  Feel free to use it in your applications.

using System.Web;
using CodeCampServer.Model;
using NHibernate;
using NHibernate.Cfg;

namespace CodeCampServer.DataAccess.Impl
{
    public class HybridSessionBuilder : ISessionBuilder
    {
        private static ISessionFactory _sessionFactory;
        private static ISession _currentSession;

        public ISession GetSession()
        {
            ISessionFactory factory = getSessionFactory();
            ISession session = getExistingOrNewSession(factory);
            Log.Debug(this, "Using ISession " + session.GetHashCode());
            return session;
        }

        private ISessionFactory getSessionFactory()
        {
            if (_sessionFactory == null)
            {
                Configuration configuration = GetConfiguration();
                _sessionFactory = configuration.BuildSessionFactory();
            }

            return _sessionFactory;
        }

        public Configuration GetConfiguration()
        {
            var configuration = new Configuration();
            configuration.Configure();
            return configuration;
        }

        private ISession getExistingOrNewSession(ISessionFactory factory)
        {
            if (HttpContext.Current != null)
            {
                ISession session = GetExistingWebSession();
                if (session == null)
                {
                    session = openSessionAndAddToContext(factory);
                }
                else if (!session.IsOpen)
                {
                    session = openSessionAndAddToContext(factory);
                }

                return session;
            }

            if (_currentSession == null)
            {
                _currentSession = factory.OpenSession();
            }
            else if (!_currentSession.IsOpen)
            {
                _currentSession = factory.OpenSession();
            }

            return _currentSession;
        }

        public ISession GetExistingWebSession()
        {
            return HttpContext.Current.Items[GetType().FullName] as ISession;
        }

        private ISession openSessionAndAddToContext(ISessionFactory factory)
        {
            ISession session = factory.OpenSession();
            HttpContext.Current.Items.Remove(GetType().FullName);
            HttpContext.Current.Items.Add(GetType().FullName, session);
            return session;
        }
    }
}

NHibernate will automatically look for a file in the current AppDomain's base location named "hibernate.cfg.xml".  This file in CodeCampServer is the following:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration  xmlns="urn:nhibernate-configuration-2.2" >
    <session-factory>
        <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
        <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
        <property name="connection.connection_string">Data Source=localhost\sqlexpress;Initial Catalog=CodeCampServer;Integrated Security=True</property>
        <property name="show_sql">false</property>
        <property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>

        <property name="query.substitutions">true 1, false 0</property>    
        <mapping assembly="CodeCampServer.DataAccess" />
    </session-factory>
</hibernate-configuration>


Trackbacks

NHibernate « Beautiful code Posted on 8.06.2008 at 1:51 AM

Pingback from NHibernate « Beautiful code

Comments

Glenn said on 7.09.2008 at 5:38 PM

Just out of curiosity, I've just started looking into NHibernate and it's my understanding that ISessionFactory is thread-safe and can be created once for the entire application (for instance in Application_Start). I see that you are creating a new ISessionFactory for every request, could you share your reasoning for that?

BTW, this stuff is Gold for me getting into NHibernate, people like you with practical experience sharing your knowledge, pure gold. Thanks!

jpalermo said on 7.09.2008 at 5:42 PM

@Glenn,

Notice that the _sessionFactory is "static". It's created once for the life of the AppDomain.

Glenn said on 7.09.2008 at 6:13 PM

Ah! Missed that one. Thanks for your clarification. Since I'm on the question-run here, is that Log4Net you're using?

Jeffrey Palermo said on 7.09.2008 at 9:36 PM

@Glenn,

Yes, I'm using Log4Net, but I have a "Log" class that wraps up Log4Net so replacing it wouldn't be that difficult. Check out CodeCampServer for all the code.

Simone Busoli said on 7.16.2008 at 3:26 AM

Hi Jeffrey, when do you close the session?

Scott Lowe said on 7.24.2008 at 5:58 AM

@Simone,

Jeffrey is closing the session in the HttpModule NHibernateSessionModule that accompanies this code on the context.EndRequest event. You can see this in the CodeCampServer codebase.

@Jeffrey

If the session is being closed by the web application in normal use, what is your strategy for automated integration testing of the repositories in isolation? It would be nice not to have to mock the HttpApplication context.

Simone Busoli said on 7.24.2008 at 5:26 PM

I don't like very much spreading the code which manages the lifetime of the session over two distinct entities.

I prefer something like the approach used in RhinoCommons.

Leave a Comment