问题
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:


回答1:
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.
回答2:
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.
来源:https://stackoverflow.com/questions/22137939/session-per-httprequest-using-autofac-nhprof-showing-multiple-session-warning