NHibernate Forge
The official new home for the NHibernate for .NET community

Effective NHibernate Session management for web apps

In this post I’ll describe a mechanism to manage nhibernate session following the widely known patter “session-per-request”.

Introduction

The session-per-request pattern is very well defined and widely used; as follows

A single Session and a single database transaction implement the processing of a particular request event (for example, a Http request in a web application).

What do we have currently?

The first thing you will notice when talking about nhibernate session management is a little interface inside NHibernate;

public interface ICurrentSessionContext
{
    ISession CurrentSession();
}

The only purpose of the implementors is to store and retrieve the current session from somewhere. This class is used by the SessionFactory of nhibernate when calling the method GetCurrentSession().

There are lot of implementations of ICurrentSessionContext but for web the two more important are:

  • WebSessionContext inside NHibernate (namespace NHibernate.Context)
  • WebSessionContext inside uNhAddIns.Web (namespace Session.Easier)

They are pretty similar but the one inside uNhAddins supports multi-session-factory scenarios.

Where do we init & store the session?

We have an httpmodule in uNhAddins which add handler for the BeginRequest as follows:

Untitleddrawing (2)

The problem

Although the afore mentioned handler does not open a session and a transaction for images or JavaScript files there might be some request to pages that will not talk with the persistence and we don’t want a OpenSession/BeginTransaction there.

Ayende already talked about this in his blog. But the problem is that he only wrote about the Session which is really light to instantiate. The problem is how do we handle the scope of the transaction which is not so light?

Alternative solutions

There are currently three solutions for this problem:

  • Use BeginRequest/EndRequest to open/close the session. Handle the transaction with AOP – attributes. I am not sure but I think this is the case for the AutoTransaction facility of castle.
  • To use Asp.Net MVC ActionFilters to Open/Close the session and the transaction.
  • There is a third which is an antipattern; using implicit transactions for most of the cases and explicit for some others.

The main problem with these approaches is that you need to explicitly put something to say that some piece of code will use a session/transaction. Even if you do it with AOP!

My new solution

My new solution is to store a Lazy<ISession> per request instead of an ISession. Initialize in the first usage and finalize in the EndRequest – only if it was used/opened.

The implementation I’ll show also support multi-session factories.

The ICurrentSessionContext looks as follows:

public class LazySessionContext : ICurrentSessionContext
{
    private readonly ISessionFactoryImplementor factory;
    private const string CurrentSessionContextKey = "NHibernateCurrentSession";

    public LazySessionContext(ISessionFactoryImplementor factory)
    {
        this.factory = factory;
    }

    /// <summary>
    /// Retrieve the current session for the session factory.
    /// </summary>
    /// <returns></returns>
    public ISession CurrentSession()
    {
        Lazy<ISession> initializer;
        var currentSessionFactoryMap = GetCurrentFactoryMap();
        if(currentSessionFactoryMap == null || 
            !currentSessionFactoryMap.TryGetValue(factory, out initializer))
        {
            return null;
        }
        return initializer.Value;
    }

    /// <summary>
    /// Bind a new sessionInitializer to the context of the sessionFactory.
    /// </summary>
    /// <param name="sessionInitializer"></param>
    /// <param name="sessionFactory"></param>
    public static void Bind(Lazy<ISession> sessionInitializer, ISessionFactory sessionFactory)
    {
        var map = GetCurrentFactoryMap();
        map[sessionFactory] = sessionInitializer;
    }

    /// <summary>
    /// Unbind the current session of the session factory.
    /// </summary>
    /// <param name="sessionFactory"></param>
    /// <returns></returns>
    public static ISession UnBind(ISessionFactory sessionFactory)
    {
        var map = GetCurrentFactoryMap();
        var sessionInitializer = map[sessionFactory];
        map[sessionFactory] = null;
        if(sessionInitializer == null || !sessionInitializer.IsValueCreated) return null;
        return sessionInitializer.Value;
    }

    /// <summary>
    /// Provides the CurrentMap of SessionFactories.
    /// If there is no map create/store and return a new one.
    /// </summary>
    /// <returns></returns>
    private static IDictionary<ISessionFactory, Lazy<ISession>> GetCurrentFactoryMap()
    {
        var currentFactoryMap = (IDictionary<ISessionFactory,Lazy<ISession>>)
                                HttpContext.Current.Items[CurrentSessionContextKey];
        if(currentFactoryMap == null)
        {
            currentFactoryMap = new Dictionary<ISessionFactory, Lazy<ISession>>();
            HttpContext.Current.Items[CurrentSessionContextKey] = currentFactoryMap;
        }
        return currentFactoryMap;
    }
}

The new HttpModule is:

public class NHibernateSessionModule : IHttpModule
{
    private HttpApplication app;
    private ISessionFactoryProvider sfp;

    public void Init(HttpApplication context)
    {
        app = context;
        sfp = (ISessionFactoryProvider) 
                  context.Application[SessionFactoryProvider.Key];
        context.BeginRequest += ContextBeginRequest;
        context.EndRequest += ContextEndRequest;
    }

    private void ContextBeginRequest(object sender, EventArgs e)
    {
        foreach (var sf in sfp.GetSessionFactories())
        {
            var localFactory = sf;
            LazySessionContext.Bind(
                new Lazy<ISession>(() => BeginSession(localFactory)), 
                sf);
        }
    }

    private static ISession BeginSession(ISessionFactory sf)
    {
        var session = sf.OpenSession();
        session.BeginTransaction();
        return session;
    }

    private void ContextEndRequest(object sender, EventArgs e)
    {
        foreach (var sf in sfp.GetSessionFactories())
        {
            var session = LazySessionContext.UnBind(sf);
            if (session == null) continue;
            EndSession(session);
        }
    }

    private static void EndSession(ISession session)
    {
        if(session.Transaction != null && session.Transaction.IsActive)
        {
            session.Transaction.Commit();
        }
        session.Dispose();
    }

    public void Dispose()
    {
        app.BeginRequest -= ContextBeginRequest;
        app.EndRequest -= ContextEndRequest;
    }
}

You can see here how we bind to the Lazy to the current context and the initializer. The BeginSession method initializes a session and a transaction.

The UnBind method returns a session only if the Lazy was initialized. If it returns something we properly commit the transaction and dispose the session.

The ISessionFactoryProvider is:

public interface ISessionFactoryProvider
{
    IEnumerable<ISessionFactory> GetSessionFactories();
}

and the SessionFactoryProvider is just an store for the constant:

public class SessionFactoryProvider
{
    public const string Key = "NHibernateSessionFactoryProvider";
}

I didn't write an implementation for ISessionFactoryProvider because I’m using castle typed factories.

The IWindsorInstaller for castle looks as follows:

public class NHibernateInstaller : IWindsorInstaller
{
    #region IWindsorInstaller Members

    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Component.For<ISessionFactory>()
                               .UsingFactoryMethod(k => BuildSessionFactory()));

        container.Register(Component.For<NHibernateSessionModule>());

        container.Register(Component.For<ISessionFactoryProvider>().AsFactory());
        
        container.Register(Component.For<IEnumerable<ISessionFactory>>()
                                    .UsingFactoryMethod(k => k.ResolveAll<ISessionFactory>()));

        HttpContext.Current.Application[SessionFactoryProvider.Key]
                        = container.Resolve<ISessionFactoryProvider>();
    }

    #endregion

    public ISessionFactory BuildSessionFactory()
    { 
        var config = new Configuration().Configure();
        //your code here :)
        return config.BuildSessionFactory();
    }
}

The only thing you have to do in NHibernate is to tell which is the CurrentSessionContextClass as follows:

configuration.Properties[Environment.CurrentSessionContextClass]
    = typeof (LazySessionContext).AssemblyQualifiedName;

Working with multiples session factories

When working with multiples session factories, the way to go is to name your components in the container:

container.Register(Component.For<ISessionFactory>()
                            .UsingFactoryMethod(...)
                            .Named("MySf1"));

Then you can start naming your Daos and explicitly overriden whenever they are injected:

container.Register(Component.For(typeof(IDao<>))
                            .ImplementedBy(typeof(Dao<>))
                            .ServiceOverrides(ServiceOverrides.ForKey("sessionFactory").Eq(MySf1))
                            .Named("SalesDao"));

container.Register(Component.For(typeof(IMyService<>))
                            .ImplementedBy(typeof(MyService<>))
                            .ServiceOverrides(ServiceOverrides.ForKey("dao").Eq(MyDao1)));

Another way is to have a factory like this one:

public interface IDaoFactory
{
     IDao<T> GetSalesDao();
     IDao<T> GetCMSDao();
}

This will work out of the box with castle typed factories, following the pattern Get[Dao component name]. With other containers you will have to implement the interface.

Remember also that NHibernate lets you name your session factory through the configuration, that is sometimes useful.

How to use the ISession from my code?

The same way I do since long time ago;

public class Dao<T> : IDao<T>
{
    private readonly ISessionFactory sessionFactory;

    public Dao(ISessionFactory sessionFactory)
    {
        this.sessionFactory = sessionFactory;
    }

    public void Save(T transient)
    {
        sessionFactory.GetCurrentSession().Save(transient);
    }

    //Other methods

Injecting the ISessionFactory instead an ISession has the following advantages:

  • It is very handy to use stateless session or a short lived session in some methods for some queries through OpenStatelessSession/OpenSession
  • The lifestyle of the Dao is not tied to the Session.  It could be even singleton.

Other session managements

In unhaddins there are various contexts;

  • PerThread
  • ConversationalPerThread
  • Web

And ways to hook such contexts;

  • In winforms; for use with the pattern Conversation per Business transaction.
  • In WCF; an OperationBehavior
  • In web an httpmodule

It is up to you to choose the combination. For instance when doing WCF that will run inside iis, you can use the operationbehavior + web. But when you do WCF out of IIS, you can use OperationBehavior + PerThread.

The important thing about this is that your DAOs are exactly the same despite the management you use.

Notes

-For non-.Net 4 projects; as Richard Birkby points out you can use the Lazy<T> inside Mono.

Finally

I hope you find this useful.

This code is not going to be in uNhAddins for now. You can copy&paste all the code from this gist.


Posted mar 03 2011, 01:09 a.m. by Jose Romaniello

Comments

Bogdan Marian wrote re: Effective NHibernate Session management for web apps
on 03-03-2011 4:05

Hello,

This is a very illuminating article, thanks for writing it.

I do have a question: why are you passing an ISessionFactory to your DAO? An ISession is not good enough ?

And a second question: if my application uses several ISessionFactories, how can I choose the right one using your ICurrentSessionContext implementation ?

nokiola wrote re: Effective NHibernate Session management for web apps
on 03-03-2011 5:41

What kind of setup/ICurrentSessionContext do you recommend for managing sessions in an WCF application where you don't have access to HttpContext?

Is there any abstraction in uNhAddIns which allows session management independently of the type of application: web, WCF, console?

Jose Romaniello wrote re: Effective NHibernate Session management for web apps
on 03-03-2011 8:06

@Bogdan Marian

Thank you for your comments i updated the article;

1. It is explained at the end of "How to use the ISession from my code?" section.

2. I added a new Section called "Working with various session"

@nokiola

Thanks for your comments too.

1. For the first question (nhibernate setup) you have to do:

configuration.Properties[Environment.CurrentSessionContextClass] = typeof (LazySessionContext).AssemblyQualifiedName;

I added this to the article.

2.I added a new section "Other session managements"

DotNetShoutout wrote Effective NHibernate Session management for web apps - NHibernate blog
on 03-06-2011 21:53

Thank you for submitting this cool story - Trackback from DotNetShoutout

codepuke wrote re: Effective NHibernate Session management for web apps
on 03-13-2011 16:42

I really like the sound of this lazy session-per-request pattern, but I am struggling to implement it using StructureMap rather than Windsor.

The Bind and Unbind methods seem to always refer to different instances of sessionFactory, so the Unbind map[sessionFactory] always fails with an unknown key exception.

I am guessing my implementation of ISessionFactoryProvider.GetSessionFactories() is incorrect.  Does anyone have a working StructureMap based implementation of the above working?

Thanks.

Jose Romaniello wrote re: Effective NHibernate Session management for web apps
on 03-13-2011 16:59

@codepuke why do you get differents instances of SessionFactory?

Each ISessionFactory must be registered as "singleton".

ISessionFactoryProvider.GetSessionFactories() should do "return container.ResolveAll<ISessionFactory>()" or the equivalent in StructureMap.

does this help?

codepuke wrote re: Effective NHibernate Session management for web apps
on 03-13-2011 17:25

@Jose - yep it does help.

It was the singleton issue.  Incorrect structuremap definition to meet this purpose.  

Perfect.  Now to play around.  :)

Thanks

mynkow wrote re: Effective NHibernate Session management for web apps
on 04-05-2011 4:53

Hi, nice article. :)

I have a question. You have 2 methods in NHibSessionModule: BeginSession (where a transaction begins) and EndSession (where the transaction.Active is always false). What I am missing?

Jose Romaniello wrote re: Effective NHibernate Session management for web apps
on 04-05-2011 7:10

I don't know about your code.. If you asked for a session somewhere in the request IsActive must be true.

There is a complete example of this here:

bitbucket.org/.../nugetspeed

using the chinook database at codeplex. Try it and let me know if it works for you.

mynkow wrote re: Effective NHibernate Session management for web apps
on 04-07-2011 6:17

10x Jose, it was my mistake. I wrap the session in unit of work. When the UoW is accepted the transaction is committed and domain events are published. So I moved the commit to the UoW accept method and I am OK. I am thinking... should I instantiate the UoW like the session, per web request? Currently I have a UoW factory which creates an instance to a thread static member. I think that I am doing this wrong... https://gist.github.com/907399

mynkow wrote re: Effective NHibernate Session management for web apps
on 07-15-2011 5:23

Hey, Jose

Do you have an idea why I get "Event handlers can only be bound to HttpApplication events during IHttpModule initialization" in IIS7, asp.net 4.0 integrated mode. The error is in the dispose() method of the http module. My web.config is perfect. Any idea how to fix it?

Leonardo Lima Silva wrote re: Effective NHibernate Session management for web apps
on 07-28-2011 0:47

Hi,

I´m trying to use this code..

But I got error on the following line

container.Register(Component.For<ISessionFactoryProvider>().AsFactory());

Like AsFactory() don´t exists....

How could I fix this??

Jose Romaniello wrote re: Effective NHibernate Session management for web apps
on 07-28-2011 7:27

AsFactory is an extension method in Castle.Facilities.TypedFactory

docs.castleproject.org/Default.aspx

Please check this guide to use all this and configure quickly

joseoncode.com/.../create-an-asp-net-mvc-application-with-windsor-and-nhibernate-at-the-speed-of-nuget

danyolgiax wrote re: Effective NHibernate Session management for web apps
on 07-28-2011 8:35

@Leonardo Lima Silva:

try to add:

using Castle.Facilities.TypedFactory;

Leonardo Lima Silva wrote re: Effective NHibernate Session management for web apps
on 07-28-2011 10:00

@Jose Romaniello and @danyolgiax

Complete right... I got it working now... but I saw that I needed this include after download @Jose Romaniello sample project on gist....

I've another problem...

I need user 2 different´s databases, and I'm using FluentNHibernate, any problem with this???

Someone can help me clear my ideas? and show me a piece of code for doing that???  

Leonardo Lima Silva wrote re: Effective NHibernate Session management for web apps
on 07-28-2011 10:10

@Jose Romaniello

Hey... sorry...

But now I got another question...

I was trying to make Castle Windsor on a ASP.NET WebForms app.. but now I saw that it isn´t possible... without use MVP approach... but I could not use it...

So... my question... is possible use your SessionManagement on a old ASP.NET WebForms app ???

If yes... how???

Jose Romaniello wrote re: Effective NHibernate Session management for web apps
on 07-28-2011 10:25

Yes, you can. how? i don't recall.... try to lookup in google how to do dependency injection in webforms. There isn't an extensibility point like the controller factory, but you can hydrate with dependencies the page object after...

To be honest i don't recall how.

linloil wrote re: Effective NHibernate Session management for web apps
on 09-27-2011 16:03

Jose, thank you for the post!

What about error handling? Don't you have to subscribe to OnError message at http module to prevent ISession lost?

Jeffrey Palermo (.com) wrote Challenging non-local session scope (session-per-request)
on 10-17-2011 10:12

I posted the following on the nhusers mailing list , and Steve Bohlen was nice enough to weigh in.  I was hoping to get broader feedback on this, so I’m posting it here.  ====================================================== First, some context

Billy McCafferty wrote S#arp Lite: The Basics
on 11-11-2011 21:10

What is S#arp Lite? S#arp Lite is an architectural framework for the development of well-designed, custom

NHibernate blog wrote S#arp Lite: The Basics
on 11-11-2011 21:29

[The motivations for releasing S#arp Lite, in relation to S#arp Architecture, are described here .] What

Hainesy wrote re: Effective NHibernate Session management for web apps
on 07-16-2012 6:50

There seems to be a problem with the Dispose method attempting to de-hook the event handlers.

I get an exception "Event handlers can only be bound to HttpApplication events during IHttpModule initialization."

Other than that, great article.

visit these guys wrote visit these guys
on 09-10-2014 0:49

Effective NHibernate Session management for web apps - NHibernate blog - NHibernate Forge

Organo Gold Review wrote Organo Gold Review
on 09-11-2014 12:23

Effective NHibernate Session management for web apps - NHibernate blog - NHibernate Forge

empire z hack wrote empire z hack
on 09-13-2014 11:46

Effective NHibernate Session management for web apps - NHibernate blog - NHibernate Forge

click for more info wrote click for more info
on 09-20-2014 20:23

Effective NHibernate Session management for web apps - NHibernate blog - NHibernate Forge

click through the following website wrote click through the following website
on 09-26-2014 10:26

Effective NHibernate Session management for web apps - NHibernate blog - NHibernate Forge

Dragon City Hack wrote Dragon City Hack
on 09-30-2014 23:17

Effective NHibernate Session management for web apps - NHibernate blog - NHibernate Forge

online shopping deals wwe wrote online shopping deals wwe
on 10-01-2014 13:57

Effective NHibernate Session management for web apps - NHibernate blog - NHibernate Forge

just click the up coming website wrote just click the up coming website
on 10-04-2014 19:11

Effective NHibernate Session management for web apps - NHibernate blog - NHibernate Forge

visit the up coming document wrote visit the up coming document
on 10-05-2014 3:58

Effective NHibernate Session management for web apps - NHibernate blog - NHibernate Forge

iam group limited wrote iam group limited
on 10-06-2014 7:31

Effective NHibernate Session management for web apps - NHibernate blog - NHibernate Forge

just click the next website page wrote just click the next website page
on 10-08-2014 14:23

Effective NHibernate Session management for web apps - NHibernate blog - NHibernate Forge

click through the up coming web page wrote click through the up coming web page
on 10-08-2014 16:18

Effective NHibernate Session management for web apps - NHibernate blog - NHibernate Forge

click the next web site wrote click the next web site
on 10-08-2014 17:47

Effective NHibernate Session management for web apps - NHibernate blog - NHibernate Forge

clash of clans triche wrote clash of clans triche
on 10-15-2014 19:57

Effective NHibernate Session management for web apps - NHibernate blog - NHibernate Forge

click through the next website page wrote click through the next website page
on 10-24-2014 14:41

Effective NHibernate Session management for web apps - NHibernate blog - NHibernate Forge

visit the following website wrote visit the following website
on 10-28-2014 7:37

Effective NHibernate Session management for web apps - NHibernate blog - NHibernate Forge

Article wrote Article
on 10-31-2014 20:15

Effective NHibernate Session management for web apps - NHibernate blog - NHibernate Forge

Powered by Community Server (Commercial Edition), by Telligent Systems