Use your container to manage Session lifetime

Last updated on May 25, 2013. Created on May 24, 2013.

There are many blog posts on how to manage the lifetime of ISession in a web application. Mainly because only ISessionFactory is thread-safe, which is a good thing because your ISession instance acts as a unit of work. For a web application the advised solution is to use the session-per-request pattern.

Even myself created one in the golden years, I’ve even posted it SourceForge a decade ago, but I’ve seem to have lost the link over the years. Coincidence, maybe?

I tend to include a container in my projects, why not let your container manage this for you? It knows about lifetime/lifestyle’s, so why reinventing the wheel again?

NHibernateConfigurationBuilder

First we create a builder around the Configuration class, because i personally hate to put everything in an Xml file and I don’t believe my database of choice is going to change in the middle of my project. This is not a needed step, but it includes some goodies

using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Mapping.ByCode;
using NHibernate.Tool.hbm2ddl;

namespace Nebula.Data
{
  public class NHibernateConfigurationBuilder
  {
    private string connectionStringName;

    public NHibernateConfigurationBuilder()
    {
      connectionStringName = "Nebula";
    }

    public NHibernateConfigurationBuilder UsingNamedConnectionString(
        string connectionStringName)
    {
      this.connectionStringName = connectionStringName;
      return this;
    }

    public Configuration Build()
    {
      var configuration = new Configuration();

      // initialize database configuration
      configuration.DataBaseIntegration(cfg =>
      {
          cfg.ConnectionStringName = connectionStringName;
          cfg.Driver<Sql2008ClientDriver>();
          cfg.Dialect<MsSql2008Dialect>();
          cfg.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
      });

      // initialize mappers
      var mapper = new ModelMapper();
      mapper.AddMappings(GetType().Assembly.GetExportedTypes());
      configuration.AddMapping(mapper.CompileMappingForAllExplicitlyAddedEntities());

      // Auto Quote all table and column names
      SchemaMetadataUpdater.QuoteTableAndColumns(configuration);
      return configuration;
    }
  }
}

The Installer

Wrap all registrations up in an installer and inject session anywhere you want

using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;
using NHibernate;
using Nebula.Data;

namespace Nebula.Bootstrapper.Installers
{
  public class NHibernateInstaller : IWindsorInstaller
  {
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
      ISessionFactory sessionfactory = new NHibernateConfigurationBuilder()
        .Build()
        .BuildSessionFactory();

      container.Register(
        Component
            .For<ISessionFactory>()
            .Instance(sessionfactory));

      container.Register(
        Component
            .For<ISession>()
            .UsingFactory<ISessionFactory, ISession>(
                factory => factory.OpenSession())
            .LifestylePerWebRequest());
    }
  }
}

For the registration of ISessionFactory i could have used a factory method, but that would delay any possible configuration mismatches until the first time a sessionfactory/session is needed.

If you have a usecase for IStatelessSession, append this to the installer and you are good to go:

container.Register(
  Component.For<IStatelessSession>()
            .UsingFactory<ISessionFactory, IStatelessSession>(
                  factory => factory.OpenStatelessSession())
            .LifestylePerWebRequest());

Side note

The same mechanics can also be used to manage the lifetime of Entity framework’s objectcontext! Even if you are in a wcf context, just change the lifestyle of your session accordingly (see wcf facility).

Next

Who manages the transaction?

Previous

I let you know that something happened