NHibernate - Session Per Request
A common pattern when using NHibernate on web applications is to create a new session which lasts for the duration of the request. I was looking at the possibility of formalising this somehow, but that will be for another day. I'm simply going to explain my approach and post some code snippets here.
The Repository
For this particular application, I'm also using StructureMap. The data access classes take an IRepository interface in their constructor which define the following methods and property:
void Configure(string connectionString, Func<System.Collections.IDictionary> requestVariables);
void BeginSession();
void EndSession(bool commit);
ISession Session { get; }
Where ISession is the NHibernate session interface. The DAOs then simply use the Session property to perform the necessary queries.
StructureMap is configured using the default conventions and so will return the same IRepository instance for the life of the application. The implementation is pretty simple (I may put it up on GitHub at some point):
Configurebuilds the session factory.BeginSessionopens a new session and callsBeginTransactionon the session. The session is added into the dictionary returned by therequestVariablesfunction (more on that below).EndSessiongets the session out of therequestVariablesand commits or rolls-back the transactions, depending on the argument. Also, the transaction and session are cleaned-up.
HttpContext
Clearly, the function returning the IDictionary is of importance in relation to managing the session; also when the Begin/End calls are made.
In our Application_Start method, after StructureMap has been configured, we configure the repository:
ObjectFactory.GetInstance<IRepository>().Configure(
WebConfigurationManager.ConnectionStrings[connString].ConnectionString,
() => HttpContext.Current.Items);
The second argument is where the repository will store the session…i.e. it will use the current HttpContext's items. This dictionary is therefore going to be specific to the particular request. The reason for this indirection is two-fold (rather than directly referencing HttpContext in the Repository class). Firstly, the repository class is in a separate project, which isn't tied to the web, so can't assume a HttpContext exists. Secondly, it makes testing the repository simple, as all that's required is a function returning a dictionary.
Finishing it off
Finally, the easy part. In Application_BeginRequest we have:
ObjectFactory.GetInstance<IRepository>().BeginSession();
And in Application_EndRequest we have:
ObjectFactory.GetInstance<IRepository>().EndSession(Context.Error == null)
This gives up a reasonably simple session-per-request implementation, committing the transaction only if no errors have occurred whilst processing the request.
