Autofac Registrations for NHibernate's ISessionFactory and ISession

Jun 2, 2010 at 2:16 PM

Assuming using a PerRequestTask to manage NHibernate Sessions turns out to be a smart idea over an HttpModule, I am wondering how one registers the ISessionFactory and ISession with the container.

First, the example at http://weblogs.asp.net/rashid/archive/2010/05/19/mvcextensions-perrequesttask.aspx doesn't seem right to me. If I update the code using the latest release of mvcextensions it will probably look like:

 

    public class ManageNHibernateSession : PerRequestTask
    {
        private readonly IServiceLocator _locator;
        private ISession _session;

        public ManageNHibernateSession(IServiceLocator locator)
        {
            _locator = locator;
        }

        public override TaskContinuation Execute()
        {
            var factory = _locator.GetInstance<ISessionFactory>();
            _session = factory.OpenSession();
            return TaskContinuation.Continue;
        }

        protected override void DisposeCore()
        {
            _session.Close();
            _session.Dispose();
        }
    }

 

I don't know for sure as I am banging my head on this error this morning and can't get any further. The problem I see with the code above is that the ISession grabbed by the PerRequestTask never gets loaded into the container. Hence, Autofac will create a new ISession as soon as a component needs one. This means we have two sessions created per request and the one created by the PerRequestTask will probably be useless. The PerRequestTask will probably need to be re-written to get an ISession and not an ISessionFactory as such:

 

    public class ManageNHibernateSession : PerRequestTask
    {
        private readonly IServiceLocator _locator;
        private ISession _session;

        public ManageNHibernateSession(IServiceLocator locator)
        {
            _locator = locator;
        }

        public override TaskContinuation Execute()
        {
            _session = _locator.GetInstance<ISession>(); <-- Subtle but important change
            return TaskContinuation.Continue;
        }

        protected override void DisposeCore()
        {
            _session.Close();
            _session.Dispose();
        }
    }

 

Since the PerRequestTask is responsible for closing and disposing of the session in its DisposeCore Method, I would think then that the ISession has to be marked as ExternallyOwned so that Autofac's nested container does not attempt to call Dispose on it. Also, given we only want 1 session per nested container it probably also needs to have a lifetime of InstancePerLifetimeScope, which works per HttpRequest. Thinking this through, registration for ISession and ISessionFactory with Autofac will probably look like:

 

builder.Register(c => SessionManager.SessionFactory).As<ISessionFactory>().SingleInstance();
builder.Register(c => c.Resolve<ISessionFactory>().OpenSession()).As<ISession>().InstancePerLifetimeScope().ExternallyOwned();

 

where SessionManager is just a static class that serves up the ISessionFactory intially for the container.

 

Does this make sense?

Regards,

Dave

 

Coordinator
Jun 2, 2010 at 4:05 PM

Hi David,

I think the code in my blog is not the best example for managing the NH session, I think you do not have to create PerRequestTask for managing it, isntead you can use the underlying container feature. For example, for Autoface we can create an Module which will register the NH Session like this:

using NHibernate;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;

using Autofac;
using MvcExtensions;

public class RegisterCustomServices : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        var sessionFactory = Fluently.Configure()
                                        .Database(MsSqlConfiguration.MsSql2008.ConnectionString(x => x.FromConnectionStringWithKey("myDatabase")))
                                        .Mappings(mapping => mapping.FluentMappings.AddFromAssembly(GetType().Assembly))
                                        .BuildSessionFactory();

        builder.RegisterInstance(sessionFactory).As<ISessionFactory>().SingleInstance();
        builder.Register(c => c.Resolve<ISessionFactory>().OpenSession()).As<ISession>().PerRequestScoped();
        builder.RegisterGeneric(typeof(IRepository<>)).As(typeof(Repository<>)).PerRequestScoped();
    }
}

Please note that the PerRequestScoped() is an extension method of the MvcExtensions rahter than autofac. Now lets say the Repository<T> accepts the NH session in the ctor.
public class Repository : IRepository
{
    private readonly ISession session;

    public Repository(ISession session)
    {
        this.session = session;
    }
}
Jun 2, 2010 at 4:48 PM

Ah, much better. Agreed.

Seemed like the PerRequestTask was fighting with Autofac since it would manage the ISession's Lifetime and dispose of it for us.

Thanks,

Dave

Jun 2, 2010 at 5:21 PM

Ooohhh.... I think there is a problem with the PerRequestScoped() extension method or I am doing something wrong. Or, maybe something has changed in the latest version of Autofac.

 

This registration:

builder.Register(c => c.Resolve<ISessionFactory>().OpenSession()).As<ISession>().PerRequestScoped();

causes the exception I mentioned here with PerRequestTasks.

 

However, when I use the following registration:

builder.Register(c => c.Resolve<ISessionFactory>().OpenSession()).As<ISession>().InstancePerLifetimeScope();

all works fine.

 

It appears you have mapped PerRequestScoped to InstancePerMatchingLifetimeScope and perhaps it needs to be mapped to InstancePerLifetimeScope.

I think this needs to be looked at as it will probably fix all problems. Right now things don't seem to work correctly with Autofac.

 

Regards,

Dave

Jun 2, 2010 at 7:31 PM
Edited Jun 2, 2010 at 7:31 PM

Just to let you know, I modified mvcextensions to use InstancePerLifetimeScope instead of InstancePerMatchingLifetimeScope and all works well, both with the error mentioned above as well as the problem I had with PerRequestTask.

Not sure if this will cause other problems, but I needed to get by these errors.

 

Regards,

Dave

 

Coordinator
Jun 3, 2010 at 5:24 PM

Pls check the tunk code, it has been fixed.