Dispose()如何知道它是因为异常而调用的?

时间:2011-03-10 06:23:46

标签: c# nhibernate exception-handling data-access-layer unit-of-work

我想编写一个简单的工作单元,其行为如下:

using (var unitOfWork = new UnitOfWork())
{
   // Call the data access module and don't worry about transactions.
   // Let the Unit of Work open a session, begin a transaction and then commit it.
}

这是我到目前为止(如果您认为我的设计错误,欢迎任何评论):

class UnitOfWork : IDisposable
{
   ISession _session;
   ITransation _transaction;
   .
   .
   .
   void Dispose()
   {
      _transaction.Commit();
      _session.Dispose();
   }
}

我想做的是在数据访问代码抛出异常的情况下回滚事务。所以Dispose()方法看起来像:

   void Dispose()
   {
      if (Dispose was called because an exception was thrown) 
      {
         _transaction.Commit();
      }
      else
      {
         _transaction.RollBack();
      }
      _session.Dispose();
   }

有意义吗?如果是这样,怎么办呢?

5 个答案:

答案 0 :(得分:6)

“Dispose()”应该与事务提交或回滚无关。您应该在Dispose()方法中处理事务。更改Dispose()方法的语义只会为您和使用您的类的其他任何人带来混乱。

事务的Commit()和RollBack()方法与Dispose()方法没有任何关系,因为这两个方法与Dispose()之间没有关联,因为无论最终如何都必须处理事务结果是。

这是用于连接和事务的正确模式。请注意Roolback(0与异常相关(而不是处置)

connection.Open();
var trasnaction = null;
try
{
  transaction = connection.BeginTransaction(); 
  ///Do Some work
  transaction.Commit();
}
catch
{
  transaction.Rollback();
}
finally
{
  if (transaction != null)
    transaction.Dispose();
  connection.Close();
}

使用Commit(),Roolback()和Dispose()方法在UnitOfWork中模仿这种模式。

答案 1 :(得分:3)

这里游戏有点晚了,但检查this post from Ayende是否有点(疯狂)解决方案:

在Dispose方法中,你只需要弄清楚它是否“干净利落” - 即在提交交易之前弄清楚是否存在未处理的异常:

public class ExceptionDetector : IDisposable
{
    public void Dispose()
    {
        if (Marshal.GetExceptionCode()==0)
            Console.WriteLine("Completed Successfully!");
        else
            Console.WriteLine("Exception!");
    }
}

答案 2 :(得分:2)

您需要UnitOfWork.Commit()块末尾的using。在UnitOfWork内,您有一个committed标记,您可以在UnitOfWork.Dispose中查看。如果该标志为false,那么您UnitOfWork.Rollback()

答案 3 :(得分:2)

Dispose的要点是它始终运行。通过使用这个commit-rollback习惯用法,你不需要知道它的区别。

using (var unitOfWork = new UnitOfWork())
{
    // use unitOfWork here - No need to worry about transactions for this code.
    unitOfWork.Commit();
}

在这里,我们看到要么抛出异常,要么提交unitOfWork。然后我们可以在UnitOfWork中有一个bool来跟踪是否执行了Commit。然后Dispose可以回滚未提交。通过这种方式,工作单元始终可以回滚或提交。

我会在任何情况下避免在Dispose中使用Commit。对于初学者来说,ITransaction.Commit方法通常可能会在错误上抛出异常 - 这是完全正常的。但是,Dispose方法不应该抛出异常。请参阅this link并在Stackoverflow上搜索有关 why 的更深入信息。

我正在大笔思考这样的事情

class UnitOfWork : IDisposable
{
   ISession _session;
   ITransation _transaction;
   bool _commitTried;

   // stuff goes here

   void Commit()
   {
      _commitTried = true;
      _transaction.Commit();
   }

   void Dispose()
   {
      if (!_commitTried) _transaction.Rollback();
      _transaction.Dispose();
      _session.Dispose();
   }
}

忘记完全调用Commit的问题我会说不是那么大,因为除非提交,客户端代码不能正常工作因为事务是Rollback'并且更改不是应用,在夹具内部或手动操作代码时会发现。

我实际上尝试在一个项目中使用lambdas语法来处理这个问题

_repository.InTransactionDo(ThisMethodIsRunInsideATransaction);

这样客户无需担心提交任何内容。我实际上最后对此感到后悔,因为它使事情变得太复杂,并希望我采用上述方法。

答案 4 :(得分:1)

毕竟,我实现了一个应该执行所有操作的方法:

class UnitOfWork : IDisposable
{
   ...
   public void DoInTransaction(Action<ISession> method)
   {
       Open session, begin transaction, call method, and then commit. Roll back if there was an exception.
   }
}