用于Nhibernate事务管理的ActionFilter是一个不错的方法

Cra*_*erz 4 nhibernate asp.net-mvc-3

我有以下包装器:

 public interface ITransactionScopeWrapper : IDisposable
{
    void Complete();
}

public class TransactionScopeWrapper : ITransactionScopeWrapper
{
    private readonly TransactionScope _scope;
    private readonly ISession _session;
    private readonly ITransaction _transaction;

    public TransactionScopeWrapper(ISession session)
    {
        _session = session;
        _scope = new TransactionScope(TransactionScopeOption.Required,
                                      new TransactionOptions {IsolationLevel = IsolationLevel.ReadCommitted});
        _transaction = session.BeginTransaction();
    }

    #region ITransactionScopeWrapper Members

    public void Dispose()
    {
        try
        {
            _transaction.Dispose();
        }
        finally
        {
            _scope.Dispose();
        }
    }

    public void Complete()
    {
        _session.Flush();
        _transaction.Commit();
        _scope.Complete();
    }

    #endregion
}
Run Code Online (Sandbox Code Playgroud)

在我的ActionFilter中,我有以下内容:

 public class NhibernateTransactionAttribute : ActionFilterAttribute
{
    public ITransactionScopeWrapper TransactionScopeWrapper { get; set; }
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
    }
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        TransactionScopeWrapper.Complete();

        base.OnActionExecuted(filterContext);
    }

}
Run Code Online (Sandbox Code Playgroud)

我正在使用Castle来管理我的ISession,使用每个Web请求的生活方式:

container.Register(
            Component.For<ISessionFactory>().UsingFactoryMethod(
                x => x.Resolve<INHibernateInit>().GetConfiguration().BuildSessionFactory()).LifeStyle.Is(
                    LifestyleType.Singleton));

container.Register(
            Component.For<ISession>().UsingFactoryMethod(x => container.Resolve<ISessionFactory>().OpenSession()).
                LifeStyle.Is(LifestyleType.PerWebRequest));

container.Register(
          Component.For<ITransactionScopeWrapper>().ImplementedBy<TransactionScopeWrapper>().LifeStyle.Is(
              LifestyleType.PerWebRequest));
Run Code Online (Sandbox Code Playgroud)

所以现在回答我的问题.

  1. 以这种方式管理交易的任何问题
  2. ActionFilter OnActionExecuting和OnActionExecuted方法是否使用相同的线程.

我问第2,因为不保证BeginRequest和EndRequest在同一个线程上运行,如果你在它们上面抛出交易,你将遇到大问题.

在我的ActionFilter中,TransactionScopeWrapper是属性注入的.

Nic*_*cht 5

您还应该考虑其他一些方面.

首先,我要说的是决定在哪里处理您的交易.请注意,如果您使用延迟加载并将数据实体传递回视图并访问配置为延迟加载的属性或引用,则会遇到问题,因为您的事务已经关闭了OnActionExecuted.虽然我知道您应该只在视图中使用视图模型,但有时实体更方便一些.无论您是否想要使用延迟加载并在视图中访问它们,您必须将事务完成移动到OnResultExecuted方法中,以便不会过早地提交它.

其次,您还应该在提交事务之前检查是否存在任何异常或模型错误.最后我用灵感来自这里这里我来处理我的NHibernate的交易最终筛选.

第三,如果您决定在处理OnResultExecuted程序中处理您的事务,如果它是对子操作的请求,则不会这样做.原因是,像我一样,我将会话限定为Web请求,但我发现子操作不算作新请求,当他们被调用时,他们尝试打开自己的会话,他们正在获取已经打开的会话上下文代替.当子动作完成后,它试图关闭ITS会话,但实际上也关闭了父视图使用的会话.这导致依赖于延迟加载数据的子操作失败后的任何逻辑.

我想通过尝试从我的应用程序中删除我的延迟加载数据,但是在我有时间这样做之前你应该知道可能出现的这些问题.

当我意识到我需要修复一些干燥问题时,我打算发布我自己的动作过滤器.足以说我正在检查filterContext.ExceptionfilterContext.ExceptionHandled查看是否有任何错误以及是否已经处理过.请注意,仅仅因为处理了异常并不意味着您的事务可以提交.虽然这对您的应用程序编码方式更为主观,但您也可能需要filterContext.Controller.ViewData.ModelState.IsValid在提交交易之前进行检查.

更新:与您不同,我使用的是StructureMap,而不是Castle for Dependency Injection,但在我的情况下,我将此行添加到gobal.asax文件中的Application_EndRequest方法作为最后一点清理.我假设城堡里有类似的东西? StructureMap.ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();

更新2:无论如何,更直接地回答你的问题.我没有看到使用像你选择的包装器有什么问题,虽然我不确定为什么你觉得需要包装它?nHibernate在处理事务本身方面做得非常好,因此我不需要另外的抽象层.你可以很容易地明确地启动你的事务OnActionExecuting并在中明确地完成它OnActionExecuted.通过检索ISession对象DependencyResolver可以消除线程中可能存在的任何问题,因为IoC容器是线程安全的,我相信,从那里你可以使用当前事务Session.Transaction并从IsActive属性中检查它的当前状态.我的理解是,这两种方法可能会在不同的线程上发生,特别是在处理继承自的类的操作时AsynController.