Session-per-HttpRequest using Autofac. NHProf showing "multiple Session" warning when not accessing ISession through DependencyResolver

StackOverflow https://stackoverflow.com/questions/22137939

Question

I've successfully implemented Session-per-HttpRequest using Autofac.

I'm unhappy with my implementation because I am going through DependencyResolver and not relying on parameters provided by AutoFac. If I rely on the ISession parameter provided by AutoFac, then I receive a warning with NHProf indicating that there are multiple Sessions in use. If I go through DependencyResolver, the warning from NHProf disappears, but the usage feels incorrect to me.

I have followed the Autofac + MVC4.0 guidelines outlined here: https://code.google.com/p/autofac/wiki/MvcIntegration

I've also used this guide as a reference. It indicates that it should be possible to accept ISession as a constructor parameter: http://slynetblog.blogspot.com/2011/04/lightweight-nhibernate-and-aspnet-mvc.html

Here's how I build my Autofac container:

public class AutofacRegistrations
{
    public static void RegisterAndSetResolver()
    {
        var containerBuilder = new ContainerBuilder();

        containerBuilder.RegisterControllers(Assembly.GetExecutingAssembly());

        //  Only generate one SessionFactory ever because it is expensive.
        containerBuilder.Register(x => new NHibernateConfiguration().Configure().BuildSessionFactory()).SingleInstance();

        //  Everything else wants an instance of Session per HTTP request, so indicate that:
        containerBuilder.Register(x => x.Resolve<ISessionFactory>().OpenSession()).As<ISession>().InstancePerHttpRequest();
        containerBuilder.Register(x => LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType)).As<ILog>().InstancePerHttpRequest();

        containerBuilder.RegisterType<NHibernateDaoFactory>().As<IDaoFactory>().InstancePerHttpRequest();
        containerBuilder.RegisterType<StreamusManagerFactory>().As<IManagerFactory>().InstancePerHttpRequest();

        //  containerBuilder.RegisterModule adds all the required http modules to support per web request lifestyle and change default controller factory to the one that uses Autofac.
        containerBuilder.RegisterModule(new AutofacWebTypesModule());

        IContainer container = containerBuilder.Build();
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
    }
}

And here is my base controller class. Note the commented out code which original accepted session as a parameter:

public abstract class StreamusController : Controller
{
    protected readonly ILog Logger;
    protected new readonly ISession Session;

    protected StreamusController(ILog logger, /*ISession session*/)
    {
        if (logger == null) throw new ArgumentNullException("logger");
        //if (session == null) throw new ArgumentNullException("session");

        Logger = logger;

        //  TODO: Is this different than passing ISession into Controller with AutoFac?
        Session = DependencyResolver.Current.GetService<ISession>();
        //Session = session;
    }

}

I am experiencing different results with NHProf based on whether I work with ISession as a parameter or access it through DependencyResolver. Why? My understanding is that these two means should be EXACTLY THE SAME!

For reference, here's my Lazy NHibernateConfiguration/ISessionFactory implementation. I don't think it's exceedingly relevant to the issue at hand:

public class NHibernateConfiguration
{
    public FluentConfiguration Configure()
    {
        string connectionString = ConfigurationManager.ConnectionStrings["default"].ConnectionString;

        FluentConfiguration fluentConfiguration = Fluently.Configure()
            .Database(MsSqlConfiguration.MsSql2008.ConnectionString(connectionString).ShowSql().FormatSql())
            .Mappings(cfg => cfg.FluentMappings.AddFromAssemblyOf<UserMapping>())
            .ExposeConfiguration(ConfigureStreamusDataAccess);

        return fluentConfiguration;
    }

    private static void ConfigureStreamusDataAccess(Configuration configuration)
    {
        //  NHibernate.Context.WebSessionContext - analogous to ManagedWebSessionContext above, stores the current session in HttpContext. 
        //  You are responsible to bind and unbind an ISession instance with static methods of class CurrentSessionContext.
        configuration.SetProperty("current_session_context_class", "web");
        configuration.SetProperty("connection.isolation", "ReadUncommitted");
        configuration.SetProperty("default_schema", "[Streamus].[dbo]");
        configuration.SetProperty("generate_statistics", "true");
    }
}

Here are screenshots of NHProf indicating multiple sessions on my CreateMultiple action and another screenshot of it not indicating multiple sessions. The first screenshot uses ISession passed in as a parameter and the second screenshot uses DependencyResolver:

enter image description here enter image description here

Was it helpful?

Solution

I'm not sure why it's happening, but you could write the registration like so:

containerBuilder.Register(x => {
    return x.Resolve<ISessionFactory>().OpenSession(); //set breakpoint here
}).As<ISession>().InstancePerHttpRequest();

and the set a breakpoint on your OpenSession() call, then debug though your code and see what the call stack looks like each time it gets called.

OTHER TIPS

Alright, so I tracked down the culprit. It wasn't immediately obvious.

I use AutoMapper to map DTO to Domain and back. This, apparently, was bad practice.

My logic looked something like:

  • Application_Start
  • AutofacRegistrations.RegisterAndSetResolver
  • AutoMapper.SetMappings

Inside of SetMappings, I needed access to my DAO factory in order to map my DTO back to a Domain: AutoMapper best practices - Should I be asking the DAO for information to fulfill mapping from DTO to domain object?

In asked DependencyResolver for my DaoFactory. This is where the problem arose. I was asking for a DaoFactory before Autofac had a chance to create a session for my current request (since I wasn't IN a request yet.) This caused it to generate an ISession prematurely in order to be able to dish out a DaoFactory.

Refactoring my code such that each Domain object was responsible for re-mapping resolved the issue. Something like:

public static PlaylistItem Create(PlaylistItemDto playlistItemDto, IPlaylistManager playlistManager)
{
    PlaylistItem playlistItem = new PlaylistItem
        {
            Cid = playlistItemDto.Cid,
            Id = playlistItemDto.Id,
            Playlist = playlistManager.Get(playlistItemDto.PlaylistId),
            Sequence = playlistItemDto.Sequence,
            Title = playlistItemDto.Title,
            Video = Video.Create(playlistItemDto.Video)
        };

    return playlistItem;
}

where I've used IoC to give the IPlaylistManager to the PlaylistItem instead of getting access to it through DependencyResolver.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top