我需要“transactionScope.Complete();”吗?

15

据我所知,使用 TransactionScope 的“正确”方法是在退出 using 块之前始终调用 transactionScope.Complete();。像这样:

Translated text:

据我了解,“正确”使用 TransactionScope 的方法是在using块结束前始终调用transactionScope.Complete()。例如:

using (TransactionScope transactionScope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadUncommitted }))
{
    //...
    //I'm using this as a NOLOCK-alternative in Linq2sql.
    transactionScope.Complete();
}

然而,我注意到代码没有加这个,而且甚至我从中学习添加它的答案也没有提到。所以我的问题是,是否必须使用它?


完整性基本上设置一个标志,表示“我没问题”,就是这样。如果您不设置它,并且没有其他代码设置它(不要忘记此类事务可能是分布式的),那么该事务将保持不确定状态,直到事务管理器决定最终结果为止。 - Simon Mourier
TransactionScope 最初基于 Microsoft Transaction Server (MTS)。它成为 COM 的“服务”(今天称为“组件服务”的一部分)。该服务的根 COM 接口是 IObjectContext。IObjectContext 基于“提交”和“完成”两个内部/不可见标志,用于“上下文/事务/活动”,并具有设置/取消这两个标志的方法。调用 transactionScope.Complete 相当于调用 IObjectContext.SetComplete()。 - Simon Mourier
3个回答

20
所以我的问题是,必须要使用吗?
在更新时需要使用CompleteCOMMIT事务,否则,事务管理器将会发出ROLLBACK指令并撤销所作的更改。
对于像你的例子这样的只读事务,我想无论是否使用Complete都没有实质性的区别。在这两种情况下,事务管理器发出的COMMITROLLBACK具有相同的净效果,即释放由事务持有的锁和资源。
虽然在只读事务中不需要调用Complete,但在我看来,这仍然是一种最佳实践。考虑那些不知道缺少Complete而随后不慎添加了数据修改代码的可怜开发人员。

如果使用只读事务(仅选择查询),如果我们不调用Complete方法,那么我们会收到TransactionInDoubtException异常吗? - Kalpesh Rajai
1
@KalpeshRajai,我进行了快速测试,没有收到“TransactionInDoubtException”异常,但没有尝试使用分布式事务。 - Dan Guzman

4

是的,你需要使用它,这是告知编译器你已成功完成任务的方式,来自Microsoft文档关于TransactionScope类:

当你的应用程序在事务中完成所有工作后,你应该调用Complete方法一次,以通知事务管理器可以提交事务。如果没有调用此方法,则会中止事务。

另外对于Complete方法:

如果没有调用此方法,则会中止事务,因为事务管理器将其解释为系统故障或在事务范围内引发的异常。但是,你还应该注意,调用此方法并不保证提交事务。它只是通知事务管理器你的状态的一种方式。


调用此方法失败将中止事务。但实际上并没有中止。(一个简单的 db.TheTable.Count();。但是...) - ispiro
在退出 using 块之前,您所做的所有更改都“存在”于数据库中,即使它们尚未持久化,尝试在 using 块内外进行相同操作但不使用 complete,您应该能够看到差异。 - Diego Osornio
没有办法进行检查,因为我不会更改数据库,只是查询它。 - ispiro
1
通常,事务范围用于链接多个事务并完成它们的所有操作或者全部不执行。所以,如果您只是查询数据库而没有进行任何修改,那么为什么需要使用事务范围呢?您能否提供一个简单的示例? - Diego Osornio
请查看问题中代码的注释和链接。我需要一个可以与Linq2sql一起使用的NOLOCK替代方案。 - ispiro

1
基本上,使用语句在编译时由C#编译器转换为此。
       TransactionScope transactionScope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadUncommitted })
        try
        {
           //your works
        }
        finally
        {
            if (transactionScope != null)
                ((IDisposable)transactionScope).Dispose();
        }

这就是你从C#中应该期望的全部...你只需要使用TransactionScope来处理你的工作。如果TransactionScope对象最初创建了事务,则由事务管理器提交事务的实际工作发生在using块中的最后一行代码之后。如果它没有创建事务,则提交发生在Transaction对象的所有者调用Commit时。此时,事务管理器调用资源管理器并通知它们基于是否在TransactionScope对象上调用了Complete方法来提交或回滚。调用此方法不能保证事务将被提交。它只是一种通知事务管理器您的状态的方式。调用Complete方法后,您将无法再使用Current属性访问环境事务,并且尝试这样做将导致抛出异常。using语句确保即使发生异常也会调用TransactionScope对象的Dispose方法。Dispose方法标记事务范围的结束。在调用此方法后发生的异常可能不会影响事务。此方法还将环境事务恢复到其先前状态。如果范围创建事务并且事务被中止,则会引发TransactionAbortedException。如果事务管理器无法达到提交决策,则会引发TransactionInDoubtException。如果事务已提交,则不会引发异常。
希望这对您有所帮助。

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