在ADO.NET/C#中为什么要在DbTransaction上使用using语句?

5

我了解隐式回滚(通常在异常发生并调用Dispose时发生)并不保证适用于所有提供程序。然而,许多示例使用以下语法:

using (DbTransactio txn = cnctn.BeginTransaction())

有什么原因吗?
2个回答

6
最简单的答案是“因为它实现了IDisposable接口”。任何实现了IDisposable接口的类型都需要适当地进行处理和释放。在这种情况下,使用程序代码最简单的方法是通过using语句进行处理。
在这个特殊情况下,答案是:由于如果出现错误,你希望事务被回滚而不是留给垃圾回收器处理。个人而言,我可能会使用catch来Rollback,但是我们应该希望Dispose()已经完成了这项工作。除非有文档记录,否则我不会依赖它。例如:
using(var tran = conn.BeginTransaction()) {
    try {
        // do stuff...
        tran.Commit();
    } catch {
        tran.Rollback();
        throw;
    }
}

请注意,在相关的TransactionScope情况下,“Dispose()未标记为已完成”是发出回滚信号的预期方式。
using(var tran = new TransactionScope()) {
    // do stuff...
    tran.Complete();
}

感谢您的回答。我的观点是,Dispose 在大多数/所有情况下是否有其他作用,除了调用 Rollback?如果我们想在 catch 子句中执行 Rollback,有没有理由调用 Dispose?我认为显式调用 Rollback 的代码编写更好。因此,对于已提交/回滚事务,Dispose 是否必要? - char m
1
@matti 强烈建议使用 Dispose(),因为它是 IDisposable。我同意在这种情况下,我更喜欢显式的 Rollback(),就像我的例子一样。但这是 除了 Dispose()之外的补充。 - Marc Gravell
谢谢。我认为这已经足够好的理由了:D 但是正如我所说,我更喜欢显式回滚。这只是有点棘手,因为如果出现问题,事务就不再处于活动状态。因此,您的代码示例会抛出关于回滚的异常,原始原因被忽略了。例如,MSDN有一个示例,在其中Rollback在catch子句中位于另一个catch子句内...这绝对是荒谬的。 - char m

5
如果您有如下代码:
DbTransactio txn = cnctn.BeginTransaction();

// do something which throws an exception

txn.Commit();

事务不会回滚,直到垃圾收集器决定收集事务对象(请注意,只有实现 IDisposable 接口的类遵循 dispose pattern 才能起作用)。

当然,您也可以这样做:

DbTransaction txn = cnctn.BeginTransaction();
try
{
    // do something which throws an exception

    txn.Commit();
}
finally  
{
    txn.Dispose();
}

但这不够易读。

using (var txn = cnctn.BeginTransaction())
{
    // do something which throws an exception

    txn.Commit();
}

编辑:

我理解隐式回滚(通常在发生异常并调用Dispose时发生)不能保证所有提供程序都支持。

我没有见过任何一个不支持它的提供程序。由于 IDbTransaction继承了IDisposable,因此所有实现都应该使用Dispose()来回滚事务。


谢谢您的回答。我明白使用的含义,但如果Dispose只是调用回滚,为什么要调用Dispose呢? - char m
如果您使用using,则无需显式调用Rollback。在我的看法中,使用带有Commit()using块可以使代码更加清晰。 - jgauffin

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