【问题标题】:Session-per-HttpRequest using Autofac. NHProf showing "multiple Session" warning when not accessing ISession through DependencyResolver使用 Autofac 的每 HttpRequest 会话。 NHProf 在不通过 DependencyResolver 访问 ISession 时显示“多会话”警告
【发布时间】:2014-03-04 06:59:13
【问题描述】:

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

我对我的实现不满意,因为我正在经历DependencyResolver 并且不依赖于 AutoFac 提供的参数。如果我依赖 AutoFac 提供的 ISession 参数,那么我会收到一条带有 NHProf 的警告,表明有多个会话正在使用中。如果我通过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");
    }
}

这里是 NProf 的屏幕截图,表明我的 CreateMultiple 操作中有多个会话,而另一个屏幕截图没有表明多个会话。第一张截图使用ISession作为参数传入,第二张截图使用DependencyResolver:

【问题讨论】:

    标签: c# session nhibernate autofac


    【解决方案1】:

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

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

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

    【讨论】:

    • 这是一个非常好的主意,也是一种非常酷的设置断点的方法,我不知道。我去看看,谢谢。
    • 如果您发现了什么,请务必告诉我们。
    【解决方案2】:

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

    我使用 AutoMapper 将 DTO 映射到域并返回。这显然是bad practice

    我的逻辑是这样的:

    • Application_Start
    • AutofacRegistrations.RegisterAndSetResolver
    • AutoMapper.SetMappings

    在 SetMappings 内部,我需要访问我的 DAO 工厂才能将我的 DTO 映射回域:AutoMapper best practices - Should I be asking the DAO for information to fulfill mapping from DTO to domain object?

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

    重构我的代码以便每个域对象负责重新映射解决了这个问题。比如:

    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 访问它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-02-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-06
      相关资源
      最近更新 更多