Thursday 18 June 2015

Contextual NHibernate Sessions

When designing classes that will use an NHibernate session, the typical approach would be to inject the session via the class's constructor:
 public MyClass(ISession session)  
 {  
   _session = session;  
 }  
This works well when the object's lifetime is typically short. I.e. the object is created, it does something and then it's disposed. However, this approach doesn't work so well when an object's lifetime might be lengthy or unpredictable. For example, in a Windows Forms application, a navigation presenter, dependent on an NHibernate session, might live for the lifetime of the application. The problem with the design that injects the session into this presenter at construction is that the session also lives for the lifetime of the application. Furthermore, this presenter cannot share the session with other components during the execution of a use case/business transaction, a concept that is considered to be the ideal use of sessions in a smart client application, as the presenter's session might have been injected long before the start of the other use cases.

To address this problem, NHibernate supports the concept of contextual sessions. This feature works by creating a session and then 'binding' it to the SessionFactory. Classes that want to use a session within a shared context access the session via the SessionFactory's GetCurrentSession() method, rather than opening a new session via the OpenSession() method.

To use contextual sessions, the functionality must be enabled via a config parameter:
 <property name="hibernate.current_session_context_class">call</property>  
I'm using the 'call' context in this case. The documentation has more detail on this and the other contexts available. Using this parameter enables me to use the static CurrentSessionContext class to define the context of a session and bind it to the SessionFactory:
 var session = sessionFactory.OpenSession();  
 CurrentSessionContext.Bind(session);  
Now any objects that want to use the session within the defined context can access it via the SessionFactory, rather than opening their own:
 var session = sessionFactory.GetCurrentSession();