C# 使用Autofac的每个HttpRequest的会话。NHProf“显示”;“多个会话”;不通过DependencyResolver访问ISession时发出警告

C# 使用Autofac的每个HttpRequest的会话。NHProf“显示”;“多个会话”;不通过DependencyResolver访问ISession时发出警告,c#,session,nhibernate,autofac,C#,Session,Nhibernate,Autofac,我已经使用Autofac成功地实现了每个HttpRequest的会话 我对我的实现不满意,因为我正在经历依赖解析程序,并且不依赖AutoFac提供的参数。如果我依赖AutoFac提供的ISession参数,那么我会收到NHProf发出的警告,指示有多个会话正在使用。如果我通过DependencyResolver,NHProf发出的警告将消失,但我觉得用法不正确 我遵循了此处概述的Autofac+MVC4.0指南: 我还将本指南用作参考。它表示可以接受ISession作为构造函数参数: 以下是我如

我已经使用Autofac成功地实现了每个HttpRequest的会话

我对我的实现不满意,因为我正在经历
依赖解析程序
,并且不依赖AutoFac提供的参数。如果我依赖AutoFac提供的
ISession
参数,那么我会收到NHProf发出的警告,指示有多个会话正在使用。如果我通过
DependencyResolver
,NHProf发出的警告将消失,但我觉得用法不正确

我遵循了此处概述的Autofac+MVC4.0指南:

我还将本指南用作参考。它表示可以接受ISession作为构造函数参数:

以下是我如何构建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));
    }
}
公共类自动传真注册
{
公共静态无效注册表AndSetResolver()
{
var containerBuilder=新的containerBuilder();
containerBuilder.RegisterController(Assembly.getExecutionGassembly());
//仅生成一个SessionFactory,因为它非常昂贵。
Register(x=>newNHibernateConfiguration().Configure().BuildSessionFactory()).SingleInstance();
//其他所有内容都希望每个HTTP请求都有一个会话实例,因此请指出:
containerBuilder.Register(x=>x.Resolve().OpenSession()).As().InstancePerHttpRequest();
containerBuilder.Register(x=>LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType)).As().InstancePerhtPrequest();
containerBuilder.RegisterType().As().InstancePerHttpRequest();
containerBuilder.RegisterType().As().InstancePerHttpRequest();
//containerBuilder.RegisterModule添加所有必需的http模块以支持每个web请求的生活方式,并将默认控制器工厂更改为使用Autofac的工厂。
RegisterModule(新的AutofacWebTypesModule());
IContainer container=containerBuilder.Build();
SetResolver(新的AutofacDependencyResolver(容器));
}
}
这是我的基本控制器类。请注意注释掉的代码,该代码最初接受会话作为参数:

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

}
公共抽象类StreamusController:Controller
{
受保护的只读ILog记录器;
受保护的新只读会话;
受保护的StreamusController(ILog记录器,/*ISession会话*/)
{
如果(logger==null)抛出新的ArgumentNullException(“logger”);
//如果(session==null)抛出新的ArgumentNullException(“session”);
记录器=记录器;
//TODO:这与使用AutoFac将ISession传递到控制器不同吗?
会话=DependencyResolver.Current.GetService();
//会话=会话;
}
}
根据我是将ISession用作参数还是通过DependencyResolver访问它,我在NHProf中体验到不同的结果。为什么?我的理解是这两种方法应该完全相同

作为参考,这里是我的惰性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");
    }
}
公共类NHibernateConfiguration
{
public FluentConfiguration Configure()
{
string connectionString=ConfigurationManager.connectionString[“默认值”]。connectionString;
FluentConfiguration FluentConfiguration=Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(ConnectionString.ShowSql().FormatSql())
.Mappings(cfg=>cfg.FluentMappings.AddFromAssemblyOf())
.ExposeConfiguration(配置StreamUSDataAccess);
返回流配置;
}
私有静态void配置StreamUSDataAccess(配置)
{
//NHibernate.Context.WebSessionContext-类似于上面的ManagedWebSessionContext,将当前会话存储在HttpContext中。
//您负责使用类CurrentSessionContext的静态方法绑定和解除绑定ISession实例。
SetProperty(“当前会话上下文类”、“web”);
SetProperty(“connection.isolation”、“ReadUncommitted”);
SetProperty(“默认的_模式”,“[Streamus].[dbo]”;
SetProperty(“生成_统计数据”、“true”);
}
}
以下是NHProf的屏幕截图,表示我的CreateMultiple操作上有多个会话,另一个屏幕截图不表示多个会话。第一个屏幕截图使用传入的ISession作为参数,第二个屏幕截图使用DependencyResolver:


我不知道为什么会发生这种情况,但您可以这样写注册:

containerBuilder.Register(x=>{
返回x.Resolve().OpenSession();//在此处设置断点
}).As().InstancePerHttpRequest();

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

好的,所以我找到了罪魁祸首。这不是很明显

我使用AutoMapper将DTO映射到域和域。显然,这是一个错误

我的逻辑是这样的:

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;
}
  • 应用程序启动
  • AutofacRegistrations.RegisterAndSetResolver
  • AutoMapper.SetMappings
在SetMappings内部,我需要访问我的DAO工厂,以便将我的DTO映射回域:

在我的DaoFactory中,请求DependencyResolver。这就是问题所在。在Autofac有机会为我当前的请求创建会话之前(因为我还没有在请求中),我请求了一个DAOFTORY。这导致它过早地生成ISession,以便能够提供DAOFTORY

重构我的代码,使每个域对象负责