2
votes

I'm writing a web app using ASP.NET MVC 2 and picked NHibernate as my ORM. I basically learned my basics from watching the Summer of NHibernate series, and have adopted the authors session per request strategy for managing a session (Ep 13). Things seem to work well, but I'm concerned about whether this is a functional real world approach to managing a session and being thread safe. If not then I'm open to other examples.

I've added code to elaborate. Here is my code that sets up the SessionFactory:

public class NHibernateSessionManager
{
    public static readonly ISessionFactory SessionFactory;

    static NHibernateSessionManager()
    {
        try
        {
            Configuration cfg = new Configuration();

            if (SessionFactory != null)
                throw new Exception("trying to init SessionFactory twice!");

            SessionFactory = cfg.Configure().BuildSessionFactory();
        }
        catch (Exception ex)
        {
            Console.Error.WriteLine(ex);
            throw new Exception("NHibernate initialization failed", ex);
        }
    }
    public static ISession OpenSession()
    {
        return SessionFactory.OpenSession();
    }
}

And this is where I have the web request start and stop the transaction:

public class NHibernateSessionPerRequestModule : IHttpModule
{
    public void Dispose()
    {
    }

    public void Init(HttpApplication context)
    {
        context.BeginRequest +=new EventHandler(Application_BeginRequest);
        context.EndRequest +=new EventHandler(Application_EndRequest);
    }

    private void Application_BeginRequest(object sender, EventArgs e)
    {
        ISession session = NHibernateSessionManager.OpenSession();
        session.BeginTransaction();
        CurrentSessionContext.Bind(session);
    }
    private void Application_EndRequest(object sender, EventArgs e)
    {
        ISession session = CurrentSessionContext.Unbind(NHibernateSessionManager.SessionFactory);
        if(session != null)
            try
            {
                session.Transaction.Commit();
            }
            catch (Exception)
            {
                session.Transaction.Rollback();
            }
            finally
            {
                session.Close();
            }
    }
}

And this is how I would grab a session from the session factory for one of my repositories in a Controller class:

CompanyRepository _companyRepository = new CompanyRepository(NHibernateSessionManager.SessionFactory.GetCurrentSession());
2

2 Answers

1
votes

I was curious so I googled around.

HttpContext.Items can be used by multiple threads.

Nhibernate Sessions are not thread safe.

And then there is there answer: ( possible dupe ) NHibernate thread safety with session

So it seems this recipe for disaster can be managed effectively. Because you didn't post the code that Summer of Nhibernate uses its hard to say whether your implementation won't fall over.

1
votes

Taking a page from jfar I think I finally taught myself to fish (or maybe cast my pole in the right direction) on the subject and found some good articles that shed some light to elaborate on how the Summer of NHibernate scheme works. Googling around to find more details on NHibernate and Contextual Sessions helped me get a better grasp of the topic. I'm still new to NHibernate (and web programming in general) so please bear with me if this question and answer seem elementary.

It seems the Summer of NHibernate scheme is thread safe. My explanation will reference names from my code sample above. The SessionFactory variable is static, so its accessible from every thread, it is however a thread safe object (whereas as mentioned the ISession is not thread safe). The NHibernateSessionPerRequestModule establishes two events that will fire on a web request (Application_BeginRequest) and at the end of one (Application_EndRequest). Application_BeginRequest will grab an ISession object from which you can then bind it to the current context. That helps ensure that the ISession object is used for the duration of the web request and only for that thread. After a web request begins, an ISession is then retrievable with the .GetCurrentSession() for use with the _companyRepository. The Application_BeginRequest will also begin the transaction for you. The Application_EndRequest will unbind the ISession object, commit all changes at the end of your web request, and close the ISession.

Peter Wigle has an article which shows a similar example of how to implement session management: http://pwigle.wordpress.com/2008/11/21/nhibernate-session-handling-in-aspnet-the-easy-way

In that article the Global.asax is used to specify events for binding and unbinding instead of creating an IHttpModule.

Ayende has a nice clean example that uses the HttpContext to manage your ISessions: http://ayende.com/Blog/archive/2009/08/05/do-you-need-a-framework.aspx

If I am way off base and or am not understanding something please feel free to interject, but as far as I can tell so long as a different ISession is being binded to each thread context it will be thread safe (unless there is a need to have a cross threaded ISession).