使用Autofac进行Session-per-HttpRequest。 当不通过DependencyResolver访问ISession时,NHProf显示“多会话”警告

我已经使用Autofac成功实现了Session-per-HttpRequest。

我对我的实现不满意,因为我正在通过DependencyResolver而不依赖于AutoFac提供的参数。 如果我依赖AutoFac提供的ISession参数,那么我会收到NHProf的警告,表明正在使用多个Sessions。 如果我通过DependencyResolver ,NHProf的警告会消失,但使用方法对我来说不正确。

我遵循了此处概述的Autofac + MVC4.0指南: https : //code.google.com/p/autofac/wiki/MvcIntegration

我也使用本指南作为参考。 它表明应该可以接受ISession作为构造函数参数: http : //slynetblog.blogspot.com/2011/04/lightweight-nhibernate-and-aspnet-mvc.html

以下是我构建Autofac容器的方法:

 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().OpenSession()).As().InstancePerHttpRequest(); containerBuilder.Register(x => LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType)).As().InstancePerHttpRequest(); containerBuilder.RegisterType().As().InstancePerHttpRequest(); containerBuilder.RegisterType().As().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)); } } 

这是我的基本控制器类。 注意注释掉的代码,原始接受的会话作为参数:

 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(); //Session = session; } } 

根据我是使用ISession作为参数还是通过DependencyResolver访问它,我在NHProf中遇到了不同的结果。 为什么? 我的理解是这两种方式应该完全相同!

作为参考,这是我的Lazy NHibernateConfiguration / ISessionFactory实现。 我认为这与手头的问题无关:

 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()) .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"); } } 

以下是NHProf的屏幕截图,指示我的CreateMultiple操作上的多个会话,另一个屏幕截图不指示多个会话。 第一个屏幕截图使用传入的ISession作为参数,第二个屏幕截图使用DependencyResolver:

在此处输入图像描述在此处输入图像描述

我不确定为什么会这样,但你可以这样写注册:

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

并在OpenSession()调用上设置断点,然后通过代码调试并查看每次调用时调用堆栈的样子。

好吧,所以我追查了罪魁祸首。 这并不是很明显。

我使用AutoMapper将DTO映射到域并返回。 显然,这是不好的做法 。

我的逻辑看起来像:

  • 的Application_Start
  • AutofacRegistrations.RegisterAndSetResolver
  • AutoMapper.SetMappings

在SetMappings内部,我需要访问我的DAO工厂才能将我的DTO映射回域: AutoMapper最佳实践 – 我是否应该向DAO询问信息以实现从DTO到域对象的映射?

在DependencyResolver中询问我的DaoFactory。 这就是出现问题的地方。 在Autofac有机会为我当前的请求创建一个会话之前,我要求一个DaoFactory(因为我还没有请求。)这导致它过早地生成一个ISession,以便能够淘汰DaoFactory。

重构我的代码,使每个Domain对象负责重新映射解决了问题。 就像是:

 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; } 

我已经使用IoC将IPlaylistManager提供给PlaylistItem,而不是通过DependencyResolver访问它。