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=localhostsqlexpress;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>