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

时间:2012-03-21 21:11:40

标签: asp.net-mvc-3 nhibernate

我有以下包装器:

 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
}

在我的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);
    }

}

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

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

现在问我的问题。

  1. 以这种方式管理交易的任何问题
  2. ActionFilter OnActionExecuting和OnActionExecuted方法是否使用相同的线程。
  3. 我问第2号因为不保证BeginRequest和EndRequest在同一个线程上运行,如果你在它们上面交易,你就会遇到大问题。

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

2 个答案:

答案 0 :(得分:5)

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

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

其次,您还应该在提交事务之前检查是否存在任何异常或模型错误。我最终使用来自herehere的灵感作为我的最终过滤器来处理我的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中明确地完成交易。通过DependencyResolver检索ISession对象,您可以消除线程安全所带来的任何问题,因为IoC容器是线程安全的,我相信,从那里您可以使用Session.Transaction获取当前事务并检查它来自IsActive属性的当前状态。我的理解是,这两种方法可能会在不同的线程上发生,特别是在处理继承自AsynController的类的操作时。

答案 1 :(得分:0)

我遇到了这种方法的问题。如果你使用“@ Html.Action(”TestMethod“,”TestController“)”它会怎么做?

至于我,我更喜欢使用显式交易调用:

using (var tx = session.BeginTransaction())
{
    // perform your insert here
    tx.Commit();
}

线程安全是什么,我也想知道。