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



我有以下包装器:

 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,使用每个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));

那么现在来回答我的问题。

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

我问第二个问题,因为BeginRequest和EndRequest不能保证在同一个线程上操作,如果你在它们上抛出事务,你会遇到大问题。

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

您还应该研究其他一些方面。

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

其次,在提交事务之前,您还应该检查是否存在任何异常或模型错误。我最终使用了来自这里和这里的灵感作为处理nHibernate事务的最终过滤器。

第三,如果您决定在OnResultExecuted处理程序中处理您的事务,那么如果它是对子操作的请求,则不这样做。原因是,和你一样,我将会话的范围限定为web请求,但我发现子操作不算作新请求,当它们被调用并试图打开自己的会话时,它们得到的是已经打开的会话上下文。当子操作完成时,它试图关闭ITS会话,但实际上也在关闭父视图使用的会话。这导致依赖于延迟加载数据的子操作之后的任何逻辑也会失败。

当涉及到视图时,我想仔细检查并尝试从我的应用程序中删除我的懒惰加载数据,但在我有时间这样做之前,你应该意识到可能出现的这些问题。

当我意识到我有一些DRY问题需要解决时,我正要发布我自己的动作过滤器。可以说,我正在检查filterContext.ExceptionfilterContext.ExceptionHandled,以查看是否存在任何错误,以及它们是否已经被处理。请注意,仅仅因为处理了异常并不意味着您的事务可以提交。尽管这对你的应用程序的编码方式更为主观,但你也可能想在提交交易之前检查filterContext.Controller.ViewData.ModelState.IsValid

UPDATE:与您不同的是,我使用的是StructureMap,而不是Castle进行依赖注入,但在我的情况下,我在gobal.asax文件中将这一行添加到了Application_EndRequest方法中,作为清理的最后一步。我想城堡里也有类似的东西吧?StructureMap.ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();

更新2:无论如何,这是对您问题的更直接的回答。我不认为像你选择的那样使用包装器有什么错,尽管我不确定你为什么觉得有必要包装它?nHibernate在处理事务本身方面做得很好,所以对我来说,这周围的另一个抽象层似乎是不必要的。你可以很容易地在OnActionExecuting中显式启动事务,并在OnActionExecuted中显式完成它。通过DependencyResolver检索ISession对象,您可以消除对线程的任何担忧,因为我相信IoC容器是线程安全的,从那里您可以使用Session.Transaction获取当前事务,并从IsActive属性检查其当前状态。我的理解是,这两个方法可能出现在不同的线程上,特别是在处理从AsynController继承的类上的操作时。

我遇到了这样一个方法的问题。如果您使用"@Html.Action("TestMethod"、"TestController")",它会做什么?

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

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

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

最新更新