ASP.net MVC 过滤器中的 NHibernate 工作单元使用 AutoFac

NHibernate unit of work in ASP.net MVC filter using AutoFac

我正在尝试使用 NHibernate 和事务为每个请求创建一个工作单元。我在 MVC 中有一个全局应用的过滤器。它看起来像下面这样。

public class TransactionAttribute : ActionFilterAttribute
{
    private ISession _session;

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        //Constructor does NOT get run per request. 
        _session = DependencyResolver.Current.GetService<ISession>();
        _session.FlushMode = FlushMode.Commit;
        _session.BeginTransaction();

        base.OnActionExecuting(filterContext);
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (filterContext.Exception == null)
        {
            _session.Transaction.Commit();
        }
        else
        {
            _session.Transaction.Rollback();
        }

        _session.Dispose();

        base.OnActionExecuted(filterContext);
    }
}

请注意,我正在使用依赖项解析器来获取服务,这意味着它调用每个请求而不是缓存的构造函数,或者我相信是这样。

现在我的 Autofac 模块如下所示:

public class AutoFacModule
{
    public static IContainer Build()
    {
        return Build(new ContainerBuilder());
    }

    public static IContainer Build(ContainerBuilder builder)
    {

        // You can register controllers all at once using assembly scanning...
        builder.RegisterControllers(typeof(MvcApplication).Assembly);
        builder.RegisterModule<AutofacWebTypesModule>();

        builder.Register(x => NHibernateSetup.CreateSessionFactory()).SingleInstance();
        builder.Register(x => x.Resolve<ISessionFactory>().OpenSession()).InstancePerHttpRequest();

        //Build Registry. 
        var returnContainer = builder.Build();

        DependencyResolver.SetResolver(new AutofacDependencyResolver(returnContainer));

        return returnContainer;
    }
}

因此,我认为我会为每个 HTTP 请求获得一个新的 _session。但事实似乎并非如此。如果我在 MVC 操作中设置一个断点,并加载一个页面,等待它到达断点,然后启动一个新选项卡并加载同一页面,等待它到达断点,然后让两者继续。我得到一个错误:

Transaction not successfully started

当我尝试提交时。我认为这是因为第一个请求已经提交了事务。这似乎是因为两个请求共享一个 ISession 而不是每个请求都是唯一的。

发帖到 Whosebug 的行为突然让一切都变得有意义了。

所以出于某种原因,它与全局注册过滤器有关。因为有点懒,所以做了以下

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new TransactionAttribute());
}

所以基本上将每个请求包装在一个事务中。这似乎在使用 DependencyResolver 时不起作用。如果我删除它,并直接在方法上添加属性,事情就会开始起作用。

作为站点说明,我还发现了一种不使用 Autofac 的构造函数来注入属性的不同方法:http://alexmg.com/filterattribute-property-injection-in-autofac-mvc-3-integration/ 这似乎是在 autofac 下处理过滤器时的默认方式。

关于这种方法的一些事情让我有点恼火,因为像这样的 属性 public 看起来有点奇怪(因为它真的不应该由任何人设置,除了在构造函数中)。但是,它确实使事情能够进行单元测试(尽管我假设依赖项解析器方式也可以进行单元测试)。

所以现在,我将把属性直接放在操作上。但它可能需要被研究,因为像 logging/error 这样的处理可能需要注入肯定会在 globalfilters 集合中?