Tom Marien

This might not be the blog you're looking for!

Use Your Container to Manage Session Lifetime

| Comments

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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:

1
2
3
4
5
6
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).

Comments