如果DbTransaction的Commit()或Rollback()抛出异常,应该如何处理?

3

我正在使用Visual Studio 2012和MS SQL Server 2008 R2。

在我的代码中,我正在使用DbConnectionDbTransaction。这是我的代码:

DbConnection dbConnection = null;
DbTransaction dbTransaction = null;

try
{
   dbConnection = DbProviderFactories.GetFactory("System.Data.SqlClient").CreateConnection();
   dbConnection.ConnectionString = connectionString;
   dbConnection.Open();
   dbTransaction = dbConnection.BeginTransaction();
   // do my work using dbConnection and dbTransaction
   dbTransaction.Commit();
} 
catch (MyWorkFailedException mwfe)
{
   dbTransaction.Rollback();
   throw;
}
finally
{
   if (dbConnection != null)
   {
      dbConnection.Close();
      dbConnection.Dispose();
   }
}

dbTransaction.Commit();dbTransaction.Rollback();是否可能会抛出异常?

如果是,那么如何在我的代码中处理它?C#程序员通常如何处理这种情况?或者他们不处理这种情况?


3
为什么你的代码里没有一个using关键字? - Sergei Rogovtcev
@SergRogovtsev 不是我使用 try finally 的方式与使用 using 来处理 DbConnection 相同吗?如果是的话,那只是一种个人偏好和旧习惯。 - ChumboChappati
1
虽然大部分都是一样的,但你很容易犯错(例如,在代码中你可能试图回滚一个不存在的事务)。using 是一个很好的模式,你应该利用它。 - Sergei Rogovtcev
@sergRogovtsev "使用using是一个好的模式,你应该充分利用它" -> 但在WCF客户端中并非总是如此(已知的错误),你应该将其包装在try catch finally中以显式处理dispose。 - bet
@bet,这不是一个WCF客户端。 - Sergei Rogovtcev
3个回答

5
是的,CommitRollback可能抛出异常。不过,这些异常应该被传递并记录或显示给用户作为错误信息。如何处理错误完全取决于您,但通常情况下这些错误是由于连接关闭引起的。其次,您应该利用using
using (var dbConnection  = DbProviderFactories.GetFactory("System.Data.SqlClient").CreateConnection())
{
    dbConnection.ConnectionString = connectionString;
    dbConnection.Open();
    using (var dbTransaction = dbConnection.BeginTransaction())
    {
        //Do work here

        dbTransaction.Commit();
    }
}

DbTransaction会在其dispose方法中自动回滚(假设它没有被提交)。这段代码抛出的异常通常是您无法优雅地处理的。大多数情况下,它们来自SQL错误(无效语法、FK错误等)或关闭的连接。

只要您有一个良好的日志记录系统,上面的代码应该就足够了。


为什么你需要这个而不是明确使用 new SqlConnection() - abatishchev
我确实需要将异常传播到上层并显示为用户错误。在我的代码中,我已经做出了更改,在回滚后再次抛出MyWorkFailedException。假设我有一个自定义异常InfrastructureException,如果提交或回滚失败,你会如何在你的代码中抛出这个异常? - ChumboChappati

2

使用TransactionScope代替,它只有Complete()。一旦被处理过,它会回滚底层事务。任何异常都会导致底层事务回滚:

using (var scope = new TransactionScope())
using (var connection = new SqlConnection(...))
{
    // do stuff

    scope.Complete();
}

我以为只有当你的事务涉及多个数据库时才需要使用TransactionScope - ChumboChappati
实际上并不是这样,它可以与任何符合要求的提供者一起使用,并且相对于使用DbTransaction来说更加简洁。 - Sergei Rogovtcev

1
在任何方法中(包括CommitRollback),您都可以将异常处理为事务失败。如果需要,您可以在C#代码中清理一些内容。数据库清理将自动处理(至少对于符合ACID标准的数据库)。

在Rob的答案中的那个就可以。 - Sergei Rogovtcev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接