NHibernate Session handling in ASP.NET – the easy way

EDIT: Take a look at a new blog post of mine on this subject.

When I first started using the popular ORM framework NHibernate, I was a bit confused about the best way to manage NHibernate sessions in a web application. The official documentation did not provide much guidance, but I did find out that the pattern Open Session In View was the way to go. The obvious question was: how do I implement this pattern in the context of an ASP.NET application?

The implementation that was proposed in the otherwise great article NHibernate Best Practices seemed overly complicated. There must be a simpler way to do this. After reading the documenation again I found that there is a feature in NHibernate called Contextual Sessions.

The basic idea with contextual sessions is that in order to get a reference to an instance of ISession you simply call the method GetCurrentSession() that is defined in the interface ISessionFactory. The call to GetCurrentSession() is delegated to a class specified by the configuration parameter hibernate.current_session_context_class. This class implements the interface NHibernate.Context.ICurrentSessionContext. Out-of-the-box NHibernate provides one implementation of this interface, NHibernate.Context.ManagedWebSessionContext, which tracks current sessions by HttpContext. That is exactly what we want in order to implement the Open Session In View pattern.

What we need to do in order to utilize the ManagedWebSessionContext is to bind the current HttpContext to an open session at the beginning of each request and then unbind the session to the context at the end of each request. The easiest way to do this is to use the Application_BeginRequest and Application_EndRequest event handlers in Global.asax. You could of course implement your own HttpModule and do the bindind and unbinding there, if you for some reason don’t want to use Global.asax.

Now it is time to show you some code! First of all you have to implement a session manager. I have implemented the session manager as a lazy, lock-free, thread-safe singleton:

using NHibernate;
using NHibernate.Cfg;

namespace DataAccess
{

    public class SessionManager
    {
        private readonly ISessionFactory sessionFactory;
        public static ISessionFactory SessionFactory
        {
            get { return Instance.sessionFactory; }
        }

        private ISessionFactory GetSessionFactory()
        {
            return sessionFactory;
        }

        public static SessionManager Instance
        {
            get {
                return NestedSessionManager.sessionManager; }
        }

        public static ISession OpenSession()
        {
            return Instance.GetSessionFactory().OpenSession();
        }

        public static ISession CurrentSession
        {
            get
            {
                return Instance.GetSessionFactory().GetCurrentSession();
            }
        }

        private SessionManager()
        {
            Configuration configuration = new Configuration().Configure();
            sessionFactory = configuration.BuildSessionFactory();
        }

        class NestedSessionManager
        {
            internal static readonly SessionManager sessionManager =
                new SessionManager();
        }
    }
}

The binding and unbinding of the NHibernate session to the current HttpContext is done in Global.asax:

        protected void Application_BeginRequest(
            object sender, EventArgs e)
        {
            ManagedWebSessionContext.Bind(
                HttpContext.Current,
                SessionManager.SessionFactory.OpenSession());
        }

        protected void Application_EndRequest(
            object sender, EventArgs e)
        {
            ISession session = ManagedWebSessionContext.Unbind(
                HttpContext.Current, SessionManager.SessionFactory);
            if (session != null)
            {
                if (session.Transaction != null &&
                    session.Transaction.IsActive)
                {
                    session.Transaction.Rollback();
                }
                else
                    session.Flush();
                session.Close();
            }
        }

The reason for the rollback of open transaction in Application_EndRequest is to ensure that the transaction is not committed in case of an uncaught exception.

Finally we have to configure NHibernate to use the ManagedWebSessionContext, by setting the parameter current_session_context_class. There is a short name that can be used, “managed_web”. In your web.config file you add the following property:

<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
    <session-factory>
        ...
        <property name="current_session_context_class">
            managed_web
        </property>
        ...
    </session-factory>
</hibernate-configuration>

Now the property SessionManager.CurrentSession holds a reference to an open NHibernate session on every HTTP request. The usage is illustrated in the example code snippet below:

    ISession session = SessionManager.CurrentSession;
    session.BeginTransaction();
    Issue issue = new Issue();
    issue.Heading = txtHeading.Text;
    int id = (int)session.Save(issue);
    session.Transaction.Commit();
    lblCreated.Text = "Created issue with id: " + id;

As you can see this code snippet is taken from a code-behind file. In a real application you would of course put the code related to NHibernate into a DAO or Repository class.

Conclusion

What I have showed in this post is just one way of handling NHibernate sessions in an ASP.NET application. Another option which I haven’t investigated yet is to use the promising framework NHibernate Burrow. Maybe I will dig into that in another blog post.

kick it on DotNetKicks.com

About these ads
Leave a comment

16 Comments

  1. Kaz

     /  March 13, 2009

    thanks for the post. However, i do have one question that maybe you can help me out with. What happens if you need to use that object for another http request. Do you need to load the object everytime you need to use it? Let me give you an example of what i’m talking about. I have a User object and a UserProfile object. I can edit the User object and save it, but when i go to another page to edit the UserProfile object and try to save the User object(they are mapped bidirectionaly) i get an error because the User with the same id is in 2 different sessions. How can i get around this?

    Reply
    • pwigle

       /  March 19, 2009

      I guess that you store the User object in the ASP.NET session, right? I haven’t done that myself, since there are scalability issues with doing that. But if you do so, I believe that you will have to evict the object from the session it was loaded in before that session is closed. This is done by calling ISession.Evict(). Another option is to use NHibernate Burrow. With Burrow you can have sessions that span over multiple http requests.

      Reply
  2. DaRe

     /  June 10, 2009

    Tanks for sharing. I’m new to NH and I’m trying to implement a similar approach. Here’s my question:
    how do I test the correct binding to the CurrentSessionContext in the SessionManager? the binding is done only in the HttpModule and i cannot test that

    Reply
  3. Hi, How do I use Fluent NHibernate with this setup?

    I can’t seem to work out where I add the

    .Mappings(m => m.FluentMappings.AddFromAssemblyOf

    Can you help?

    Reply
    • Sosh

       /  September 3, 2009

      +1 for this question

      Reply
      • this fluent nh code works for me. i’m using sqlite with a real database file.

        private SessionManager()
        {
        sessionFactory= Fluently.Configure()
        .Database(SQLiteConfiguration.Standard.UsingFile(DbFile).ShowSql())
        .Mappings(m => m.FluentMappings.AddFromAssemblyOf())
        .ExposeConfiguration(x => x.SetProperty(“current_session_context_class”, “managed_web”))
        .BuildConfiguration()
        .BuildSessionFactory();
        }

  4. Tanks for sharing.
    Just a question: it’s acceptable eliminate the session.Flush() in the Application_EndRequest leaving to the business logic the duty to Commit() the transaction?

    Bye
    Diego

    Reply
    • pwigle

       /  August 11, 2009

      As you can see in my usage example, the business logic is responsible for committing the transaction. If a transaction is started but not committed when the HTTP request ends, the transaction will be rolled back. In my implementation, the session is flushed if there is no open transaction, but that could be left out. Most people agree that it is a good practice to always use transactions with NHibernate.

      Reply
  5. Sorry for double posting the code was really unreadable:
    private SessionManager()
    {
    sessionFactory= Fluently.Configure()
    .Database(SQLiteConfiguration.Standard.UsingFile(DbFile).ShowSql())
    .Mappings(m => m.FluentMappings.AddFromAssemblyOf())
    .ExposeConfiguration(x => x.SetProperty(“current_session_context_class”, “managed_web”))
    .BuildConfiguration()
    .BuildSessionFactory();
    }

    Reply
  6. hi pwigle
    I have a question about your implementation.

    Before I try to save data to database, do I need to include the USING statement?

    eg.

    using (ISession session = SessionManager.CurrentSession)
    {

    session.BeginTransaction();
    session.SaveOrUpdate(webPage);
    session.Transaction.Commit();
    }

    Reply
  7. I have tried to use the USING statement, it says “session” is closed. I obviously got the answer from the testing code. However, this is mean there will always be a session available for each of request.

    I’m not sure about the performance with the session available for every “Application_BeginRequest”. I have done caching for most of data, so it does not really need to get data from database.

    Is session request in “Application_BeginRequest” will slow down the website?

    Many thanks.

    Daoming.

    Reply
  8. Golam

     /  July 26, 2010

    Hi Petter,

    Thanks for the post.

    In your code SessionManager has a static member OpenSession():

    But from Application_BeginRequest the SessionManager.SessionFactory.OpenSession() is called rather SessionManager.OpenSession().

    Then what is the use of SessionManager.OpenSession()?

    –golamrabbi

    Reply
  9. pwigle

     /  July 29, 2010

    @Golam
    A good observation. The SessionManager.OpenSession() method is not needed, but it can be useful if you for some reason would like to open an additional session.

    Reply
  10. Renan

     /  May 25, 2011

    Pwigle, Great post man… thx for share your knowledge , i had the same problem as you.

    Do you have a great performance and stable using this on your web applications ?

    Reply
  1. NHibernate Bootstrapper: UnitOfWork and SessionPerRequest - NHibernate blog - NHibernate Forge
  2. NHibernate thread safety with session

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: