【问题标题】:Do I need "transactionScope.Complete();"?我需要“transactionScope.Complete();”吗?
【发布时间】:2019-05-08 12:15:04
【问题描述】:

据我了解,使用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();
}

但是,我已经看到代码在没有它的情况下也可以工作,甚至the answer I've learnt to use it from 也忽略了它。所以我的问题是,是否必须使用它?

【问题讨论】:

  • Complete 基本上设置了一个标志,上面写着“我很好”,就是这样。如果你不设置它,并且没有其他代码设置它(不要忘记这种事务可能是分布式的),那么事务将保持怀疑,直到事务管理器决定最终结果是什么。
  • TransactionScope 最初基于 Microsoft Transaction Server (MTS)。它成为 COM“服务”(今天也称为“组件服务”)的一部分。它的根 COM 接口是 IObjectContext。 IObjectContext 位于“上下文/事务/活动”的两个内部/不可见标志“提交”和“完成”上,并具有设置/取消设置这两个标志的方法。调用 transactionScope.Complete 等效于调用 IObjectContext.SetComplete()。

标签: c# sql .net sql-server linq-to-sql


【解决方案1】:

所以我的问题是,它必须使用还是不使用?

在进行更新时必须使用Complete 以便COMMIT 事务。否则,事务管理器将发出ROLLBACK 并撤消所做的更改。

对于像您的示例这样的只读事务,我认为有或没有Complete 没有实质性区别。在这两种情况下,事务管理器发出的COMMITROLLBACK 将具有相同的释放锁和事务持有的资源的净效果。

虽然不需要在只读事务中调用Complete,但恕我直言,这仍然是最佳实践。考虑一下可怜的开发人员,他后来无意中将数据修改代码添加到您的事务块中而没有看到Complete 丢失。

【讨论】:

  • 使用只读事务(只选择查询),如果我们不调用Complete方法,我们会收到TransactionInDoubtException异常吗?
  • @KalpeshRajai,我进行了快速测试,没有得到TransactionInDoubtException 异常,但没有尝试使用分布式事务。
【解决方案2】:

是的,您需要使用它,这是通过TransactionScope 类的 Microsoft 文档通知编译器您已成功完成任务的方式:

当您的应用程序完成它想要在事务中执行的所有工作时,您应该只调用一次 Complete 方法来通知事务管理器可以接受提交事务。调用此方法失败会中止事务。

同样适用于Complete 方法:

未能调用此方法会中止事务,因为事务管理器将此解释为系统故障,或事务范围内引发的异常。但是,您还应该注意,调用此方法并不能保证事务的提交。这只是将您的状态告知交易经理的一种方式。

【讨论】:

  • Failing to call this method aborts the transaction. 但事实并非如此。 (一个简单的db.TheTable.Count();。但仍然......)
  • 在您退出 using 块之前,您所做的所有更改都“存在”在数据库上,即使它们还没有持久化,请在 using 块内尝试相同的操作,然后在 using 块之外尝试没有完整和您应该能够看到差异。
  • 没有办法检查,因为我没有更改数据库,只是查询它。
  • 通常事务范围是用来链几个事务并全部完成或不完成,所以如果你只是查询数据库而不做任何修改,那你为什么需要一个ts?,你可以创建一个简单的例子?
  • 查看问题中代码中的注释,以及问题中的链接。我需要它作为与 Linq2sql 一起使用的 NOLOCK 替代品。
【解决方案3】:

基本上 using 语句在编译时由 C# 编译器转换为 this

       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。如果事务被提交,则不会抛出异常。

希望你能明白这一点

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-06
    • 2015-04-16
    • 1970-01-01
    相关资源
    最近更新 更多