我已经使用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<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));
}
}
这是我的基本控制器类。请注意注释掉的代码,原始接受的会话作为参数:
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;
}
}
根据我是使用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<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");
}
}
以下是NHProf的屏幕截图,指示我的CreateMultiple操作上的多个会话,以及另一个不指示多个会话的屏幕截图。第一个屏幕截图使用传入的ISession作为参数,第二个屏幕截图使用DependencyResolver:
答案 0 :(得分:1)
我不确定为什么会这样,但你可以这样写注册:
containerBuilder.Register(x => {
return x.Resolve<ISessionFactory>().OpenSession(); //set breakpoint here
}).As<ISession>().InstancePerHttpRequest();
并在OpenSession()调用上设置断点,然后通过代码调试,看看每次调用时调用堆栈的样子。
答案 1 :(得分:0)
好吧,所以我追查了罪魁祸首。这并不是很明显。
我使用AutoMapper将DTO映射到域并返回。这显然是bad practice。
我的逻辑看起来像:
在SetMappings内部,我需要访问我的DAO工厂才能将我的DTO映射回域:AutoMapper best practices - Should I be asking the DAO for information to fulfill mapping from DTO to domain object?
在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访问它。