为什么TransactionScope不假定操作成功?

11

TransactionScope期望调用其Complete方法。否则该事务将不会提交。

using(TransactionScope scope = new TransactionScope())
{
    /* Perform transactional work here */

    scope.Complete();
}

假设成功总是发生,这样的实现方式是否更加合适?这将意味着在标准情况下(成功),需要的代码更少。

在发生异常或调用类似于“回滚”(该方法目前不存在)的方法时,可以回滚事务。

using(TransactionScope scope = new TransactionScope())
{
    /* Perform transactional work here */

     if(problemOccurred)
     {
         scope.Rollback();
     }
}
请注意,只有在问题未导致异常的情况下才需要使用problemOccurred标志。在这种情况下,回滚将自动执行。
我对为什么使用此实现感兴趣。
更新:迄今为止,一些答案认为如果使用了我描述的实现,则需要try-catch块。这不是事实。当在using块内部未处理异常时,事务会自动回滚。这适用于现有实现和我所描述的实现。有关更多详细信息,请参见“完成事务范围”部分的此处
更新2:我最终理解了答案中所解释的内容。这不是语言结构,可以按照语言设计人员的任意方式进行解释-它是IDisposable模式的实现。如果没有调用Complete,Dispose方法内的代码将无法知道它是因为在成功执行using块内的代码还是因为发生了异常而被调用。我想象了类似以下代码的东西,其中transaction和rollback都是关键字。
transaction
{
    /* Perform transactional work here */

     if(problemOccurred)
     {
         rollback;
     }
}
当然,如果事务选项需要传递给TransactionScope,则会出现问题。

3
当我翻页后,为什么我的评论没有自动添加?我不应该需要点击“添加评论”按钮... - cjk
1
从更新-在面对异常时回滚事务的机制是,在不调用Commit的情况下到达using块的末尾。也就是说,该行为取决于您想要删除的确切功能。 - Damien_The_Unbeliever
2
你的更新是错误的 - 目前回滚的唯一原因就是你所抱怨的行为。它假设会失败 - 如果出现异常,Complete() 永远不会被调用,从而导致事务失败。没有其他神奇的机制来捕获异常。 - Sander
2
@Scott - 试着自己实现一下吧 - 你只需要实现一个实现IDisposable接口的类,并在Dispose方法中确定是否因为异常传播而被释放 - 你就会意识到为什么它不起作用。 - Damien_The_Unbeliever
1
谢谢Damien。我曾经认为TransactionScope是一种语言结构,可以按照语言设计者决定的任何方式进行解释,但它只不过是IDisposable的一种实现。 - Scott Munro
显示剩余3条评论
4个回答

15

这样,每个交易看起来都像这样:

using(TransactionScope scope = new TransactionScope())
{
  try
  {
    /* Perform transactional work here */
  }
  catch (Exception)
  {
    scope.Rollback();
    throw;
  }
}
哪种代码量更大。
编辑:
除此之外都存在风险或者不好的风格。你必须确信在提交时没有任何错误。当退出使用块时,你并不知道是因为抛出了异常还是仅仅因为到达了它的结尾。但在调用Complete时,你就知道了。
事务块语法已经是你可以做到的最简单的事情了。只需在没有任何特殊错误处理的情况下实现事务并在末尾提交即可。当发生任何错误时,您无需关心回滚。请注意,几乎每行代码都可能会抛出异常(例如,NullReference、Overflow、InvalidOperation等)。那么有什么比这更容易的呢?

1
如果您使用scope.Complete()方法,那么在什么情况下会执行回滚操作呢? - DOK
1
在TransactionScope的using块内部发生异常会自动回滚事务。这是MSDN上的一段摘录。"如果不调用此方法,事务将被中止,因为事务管理器将其解释为系统故障,或等同于在事务范围内抛出的异常。"你的try块是多余的。 - Scott Munro
这是异常链接的地址。http://msdn.microsoft.com/zh-cn/library/ms172152.aspx - Scott Munro
3
这个答案是一个自动完成TransactionScope的假设性示例。微软的评论仅适用于非自动完成版本,即当前版本。 - yfeldblum
谢谢澄清。那我想象的是一个混合体——除非出现异常,否则事务会自动提交。 - Scott Munro
1
@Scott - 这将需要在CLR内进行更改才能实现 - 你需要一种神奇的新方式来拦截未处理的异常。 - Damien_The_Unbeliever

14

这意味着您不需要在失败情况下放置手动的try/finally(或catch)块(可能带有“成功”标志)。尝试重写上面的代码以在出现错误时回滚,看看它变得多么凌乱...

基本上,正常的期望行为是只有在没有异常到达块末端时才提交。实现最简单的方法是在块末尾放置一个表示成功的方法调用。


我认为不需要使用try块 - 请看我的回答中的评论...https://dev59.com/KHI-5IYBdhLWcg3wI0x9#1999475你同意吗? - Scott Munro
1
不行,因为你提议改变那个行为。目前你不需要try/catch块,正是因为你抱怨的这种行为——除非你明确提交,否则会默认失败。 - Jon Skeet

4

如果选择了其他实现方式,那么人们会问相反的问题!

然而,一个旨在保护免受部分或不正确更新的机制中,默认行为肯定是不提交,是吧?


3

我认为,如@Jon Skeet所说,成功所需编写的代码量较少(且不那么丑陋)。 然而,从事务角度来看,我认为您应该悲观地假设,除非明确指示成功,否则您将回滚事务。 在我看来,错误提交事务比由于代码错误而意外失败地提交成功事务要糟糕得多。


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