【问题标题】:When to commit NHibernate transactions in ASP.NET MVC 2 application?何时在 ASP.NET MVC 2 应用程序中提交 NHibernate 事务?
【发布时间】:2010-08-26 15:50:04
【问题描述】:

首先,一些背景知识:我是 ASP.NET MVC 2 和 NHibernate 的新手。我正在启动我的第一个应用程序,我想使用 NHibernate,因为我来自 JSP + Struts 1 + Hibernate Web 应用程序。

似乎没有人在谈论这个,所以我想它一定很明显。我仍然摸不着头脑,因为我找不到完成以下事情的解决方案:

1) 我想使用“每个请求的会话”策略。因此,每次用户发出请求时,他都会获得一个 Nhibernate 会话,启动一个事务,当请求结束时,事务提交,并且 NHibernate 会话关闭(如果有,则返回到池中)。这保证了我的交易是原子的。

2) 当发生数据库异常(PK 违规、唯一违规等)时,我想捕获该异常,回滚我的事务并给用户一个明确的消息:如果是 PK 违规,那么该消息,同样存在所有完整性错误。

那么,我的问题是什么?我来自 Java World,在那里我使用过滤器打开会话、启动事务、处理请求,然后提交事务并关闭会话。这有效,除非发生数据库异常,并且当您在过滤器中时,由于响应已经提交,因此无法更改目标页面。

因此,当实际上事务已回滚时,用户会看到成功页面。为了避免这种情况,我必须在 Java 中编写大量数据完整性检查以防止所有完整性异常,因为我无法正确处理它们。这很糟糕,因为我正在做这项工作而不是将其留给数据库(或者我错了,我总是必须在我的应用程序中编写所有这些数据完整性代码?)。

所以我发现我猜的 IHttpModule 接口与 javax.servlet.Filter 的概念几乎相同(如果我错了,请纠正我),所以我猜我可能有同样的问题再次。

我应该将我的提交放在哪里以确保我的事务是原子的,并且当它们抛出异常时,我可以捕获它们并更改我的目标页面并向用户提供全面的消息?

到目前为止,我想出的唯一可能的解决方案是让我的 IHttpModule 启动和关闭事务,并将提交调用放在我的控制器方法的最后一行,从而能够在那里捕获异常然后返回带有消息的适当视图。现在我必须将那些提交和异常处理行复制到我所有需要提交的控制器方法中。还有关注点分离问题,我的控制器必须了解 DB,我一点也不喜欢。

有没有更好的办法?

【问题讨论】:

    标签: nhibernate asp.net-mvc-2 transactions commit


    【解决方案1】:

    如果您使用的是 ASP.NET MVC,则可以使用 ActionFilter 来实现相同的效果。

    类似的东西(这是从我的架构的不同部分拼凑而成的):

    public class TransactionalAttribute : ActionFilterAttribute, IAuthorizationFilter, IExceptionFilter
    {
    
        ITransaction transaction = NullTransaction.Instance;
        public IsolationLevel IsolationLevel { get; set; } 
    
        public TransactionalAttribute() 
        {
            IsolationLevel = IsolationLevel.ReadCommitted;
        }
    
        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            try
            {
                transaction.Commit();
                transaction = NullTransaction.Instance;
            }
            catch (Exception exception)
            {
                Log.For(this).FatalFormat("Problem trying to commit transaction {0}", exception);
            }
    
        }
    
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (transaction == NullTransaction.Instance) transaction = UnitOfWork.Current.BeginTransaction(IsolationLevel);
        }
    
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            if (filterContext.Result != null) return;
    
            transaction.Commit();
            transaction = NullTransaction.Instance;
        }
    
        public void OnAuthorization(AuthorizationContext filterContext)
        {
            transaction = UnitOfWork.Current.BeginTransaction(IsolationLevel);
        }
    
        public void OnException(ExceptionContext filterContext)
        {
            try
            {
                transaction.Rollback();
                transaction = NullTransaction.Instance;
            }
            catch (Exception exception)
            {
                Log.For(this).FatalFormat("Problem trying to rollback transaction {0}", exception);
            }
        }
    
        private class NullTransaction : ITransaction
        {
            public static ITransaction Instance { get { return Singleton<NullTransaction>.Instance; } }
    
            public void Dispose()
            {
    
            }
    
            public void Commit()
            {
            }
    
            public void Rollback()
            {
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      经过深思熟虑并与同事讨论后,我想出了一个几乎可以满足我所有要求的解决方案。

      我用我的 Java 项目实现了该解决方案,并且效果很好。我只是提出这个想法,以便每个人都可以在任何框架中使用它。

      解决方案是将提交调用放在控制器方法的最后一行,在 try-catch 块内。如果发生约束异常,您可以获得违反约束的名称。通过名称,您可以准确地告诉用户出了什么问题。我使用属性文件来存储消息以向用户显示违反了约束。属性文件的键是约束名称,值是约束违规消息。

      你可以将 commit-handle_exception-find_constraint_message 重构为一个方法,这就是我所做的。

      现在它解决了我编写代码来检查数据库完整性的问题,我相信它在属性文件中的约束违规消息非常优雅。现在,我仍然不喜欢我的控制器需要调用提交的想法,但这比编写数据库已经进行的完整性检查要好得多。

      我将继续使用 David Kemp 所说的过滤器,只是过滤器只会打开 (n)hibernate 会话和事务,然后在请求结束时关闭会话。

      非常欢迎评论。谢谢。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-17
        • 1970-01-01
        • 2021-11-05
        • 1970-01-01
        相关资源
        最近更新 更多