Managing your NHibernate Sessions
Posted by Davy Brion on 22nd June 2008
I was working on my northwind sample application (which i’ll post about as soon as i get something that’s worth showing) and i was struggling to find a clean way to manage my NHibernate sessions. I wanted a way to create a session once you enter the service layer, and that session should be available transparently to the repository implementations without actually having to constantly pass it around. But i didn’t just want to store it somewhere and have all the classes that needed the current session get it directly from that place. Also, i didn’t want the current session to be available to everyone. Another important requirement was to have maximum flexibility for writing tests. I could just use Rhino-Commons’ implementations of the UnitOfWork and Repository patterns and be done with it, but where’s the fun in that? And since this application is mostly a learning experience i figured i should try to write this myself. After a bit of searching and experimenting, i finally came up with a way that i’m happy with (for now anyways). Let’s have a look.
To illustrate what i want, take a look at this made-up method in my service layer:
public ProductCategoryDTO[] GetAllProductCategories()
{
using (ISession session = GetNewSession())
{
var repository = GetProductCategoryRepository();
var categories = repository.GetAllProductCategories();
return categories.ToDTOs();
}
}
This is just some example code, it doesn’t even work. But it’s kinda what i want… The thing is, i don’t want to deal directly with the ISession type in the service layer, but i do want the ProductCategoryRepository to use the ISession that is created within the context of this method call.
NHibernate’s ISession type is an implementation of the Unit Of Work pattern. I don’t want to use the ISession directly in my service layer because i want something that is a bit more high-level. Basically just something that would allow me to work within an NHibernate session, and provide me with the ability to flush changes to the database whenever i want to. And obviously, i want it to allow me to create a transaction as well. So i came up with the following unit of work interface:
public interface IUnitOfWork : IDisposable
{
/// <summary>
/// Flushes any changes that haven’t been executed yet
/// </summary>
void Flush();
/// <summary>
/// Creates an ITransaction instance with the ReadCommitted isolation level
/// </summary>
/// <returns></returns>
ITransaction CreateTransaction();
/// <summary>
/// Creates an ITransaction instance with the given isolation level
/// </summary>
/// <param name=”isolationLevel”></param>
/// <returns></returns>
ITransaction CreateTransaction(IsolationLevel isolationLevel);
}
This interface pretty much offers me anything i’m concerned with in my service layer. The real implementation would do a bit more though… It has to create an NHibernate session and make it available to the repositories in a clean way. Here’s the thing though… the repositories have a completely different lifetime than the NHibernate sessions. The NHibernate session has to be created when we enter a service method, and it has to be valid within the scope and context of the execution of that service method. The repositories however stay alive for the lifetime of the application so they can’t just hold a reference to an NHibernate session. Every time you call a method of a repository, it has to find out which session it should use, which is the session that is being used in the context of the current service method call. Now, we could always pass around the session but that really doesn’t look good and is cumbersome. So i basically need something that gives me access to the active nhibernate session:
public interface IActiveSessionManager
{
/// <summary>
/// Returns the active ISession for the current thread. Throws exception if there’s
/// no active ISession instance
/// </summary>
/// <returns></returns>
ISession GetActiveSession();
/// <summary>
/// Sets the active ISession for the current thread. Throws exception if there’s
/// already an active ISession instance
/// </summary>
/// <param name=”session”></param>
void SetActiveSession(ISession session);
/// <summary>
/// Clears the active ISession for the current thread.
/// </summary>
void ClearActiveSession();
}
That gives me the ability to retrieve and set the active session on a per-thread basis. After all, a service method call will be handled by one thread, so setting the active session for that current thread is a convenient way to store the session.
Now i still need something that takes care of creating the NHibernate sessions:
public interface ISessionFactory
{
/// <summary>
/// Creates a new ISession instance
/// </summary>
/// <returns></returns>
ISession Create();
}
Ok, so what do we have so far? An interface to define the functionality that a UnitOfWork should offer at the service level. An interface to store/retrieve the active session on the current thread, and finally, an interface to actually create the NHibernate session. Let’s start looking at the real implementations. Here’s the code to the SessionFactory class:
public class SessionFactory : ISessionFactory
{
private readonly NHibernate.ISessionFactory sessionFactory;
public SessionFactory()
{
Configuration configuration = new Configuration()
.Configure()
.AddAssembly(“Northwind”);
sessionFactory = configuration.BuildSessionFactory();
}
public ISession Create()
{
return sessionFactory.OpenSession();
}
}
Pretty straightforward… it initializes NHibernate and gives us the ability to ask for new sessions. So how are we going to make these sessions available to our repositories? Through the ActiveSessionManager class of course:
public class ActiveSessionManager : IActiveSessionManager
{
[ThreadStatic]
private static ISession current;
public ISession GetActiveSession()
{
if (current == null)
{
throw new InvalidOperationException(“There is no active ISession instance for this thread”);
}
return current;
}
public void SetActiveSession(ISession session)
{
if (current != null)
{
throw new InvalidOperationException(“There is already an active ISession instance for this thread”);
}
current = session;
}
public void ClearActiveSession()
{
current = null;
}
}
It basically just stores the session in a ThreadStatic field, which means that each thread will have a different static reference for this field. So if we set the active session through the SetActiveSession method in thread X, and thread Y also sets an active session, the GetActiveSession method will return the correct session instances for each thread.
So now we have everything we need to create our UnitOfWork class:
public class UnitOfWork : Disposable, IUnitOfWork
{
private readonly IActiveSessionManager activeSessionManager;
private readonly ISession session;
public UnitOfWork(ISessionFactory sessionFactory, IActiveSessionManager activeSessionManager)
{
this.activeSessionManager = activeSessionManager;
session = sessionFactory.Create();
activeSessionManager.SetActiveSession(session);
}
protected override void DisposeObjects()
{
if (session != null)
{
session.Close();
session.Dispose();
}
}
protected override void ClearReferences()
{
activeSessionManager.ClearActiveSession();
}
public void Flush()
{
session.Flush();
}
public ITransaction CreateTransaction()
{
return CreateTransaction(IsolationLevel.ReadCommitted);
}
public ITransaction CreateTransaction(IsolationLevel isolationLevel)
{
return session.BeginTransaction(isolationLevel);
}
}
When the UnitOfWork is created, it receives an ISessionFactory instance, and an IActiveSessionManager instance. It then creates a new session through the ISessionFactory and uses the IActiveSessionManager to make sure that session is the active session for the current thread. When the UnitOfWork is disposed, it closes and cleans up the session and it also uses the IActiveSessionManager to clear the active session for the current thread. Oh and it obviously also provides implementations for what it is we actually need in our service layer: flushing the changes whenever we want and creating transactions.
The ISessionFactory and IActiveSessionManager instances should stay alive as long as the application is alive. But as you could see in the implementations of those types, we didn’t write any code to deal with their lifetimes. I’m actually relying on my IoC container for that. In the class where my container is set up, you’ll find the following code:
private static void RegisterUnitOfWorkComponents()
{
Register(Component.For<ISessionFactory>()
.ImplementedBy<SessionFactory>().LifeStyle.Singleton);
Register(Component.For<IActiveSessionManager>()
.ImplementedBy<ActiveSessionManager>().LifeStyle.Singleton);
Register(Component.For<IUnitOfWork>()
.ImplementedBy<UnitOfWork>().LifeStyle.Transient);
}
ISessionFactory and IActiveSessionManager are registered as singleton instances, so whenever these types are requested, the same instances will be returned. The IUnitOfWork type is registered with a transient lifetime, so whenever it is requested, the container will create a new UnitOfWork class and pass the ISessionFactory and IActiveSessionManager instances to the constructor.
Right, we’ve taken care of creating the session, associating it with the current thread and making it available in a nice and clean way. Now we actually have to make sure our repositories can use it. In the base repository implementation, you can find the following code:
private readonly IActiveSessionManager activeSessionManager;
public Repository(IActiveSessionManager activeSessionManager)
{
this.activeSessionManager = activeSessionManager;
}
protected ISession Session
{
get { return activeSessionManager.GetActiveSession(); }
}
Whenever a repository needs a session, it just needs to use the protected Session property and it will get the session that is associated with the current thread.
So now we can rewrite our made-up service method from earlier to the following:
public ProductCategoryDTO[] GetAllProductCategories()
{
using (Container.Resolve<IUnitOfWork>())
{
var repository = Container.Resolve<ProductCategoryRepository>();
var categories = repository.GetAllProductCategories();
return categories.ToDTOs();
}
}
We know that the NHibernate session will be created, and more importantly, that it is not just globally accessible to everyone. It can only be accessed when you have a reference to an IActiveSessionManager instance. We also don’t need to pass around the session all the time so our code is a bit more concise, showing only the intent of what we’re trying to do without distracting us with details that are not relevant to that intent. We also have a lot of flexibility to write tests… i can write tests for my repositories without having to create a UnitOfWork… i can simply pass a fake IActiveSessionManager to my repositories when i’m testing them and have it return the session that i’m using for my test. All in all, this approach offers me with a lot of advantages, without ugly disadvantages. Well, at this moment i don’t really see any disadvantages so if you do see some, please let me know
Posted in Dependency Injection, Inversion Of Control, NHibernate, Patterns | 10 Comments »