gpt4 book ai didi

c# - 内部 TransactionScope 中的异常导致所有后续内部 TransactionScopes 抛出 TransactionAbortedException

转载 作者:行者123 更新时间:2023-11-30 17:02:52 25 4
gpt4 key购买 nike

我有一个应用程序,我想将多个数据库保存到一个事务中。如果他们中的任何一个失败了,我想把整个事情都推回去。但是,我想在回滚事务之前知道其中哪些失败(或成功)。

我有一个带有内部循环的外部 TransactionScope,其中循环的每次迭代都有自己的 TransactionScope。我想运行所有这些并找出哪些失败了。

例如,如果我有 5 件事我想尝试保存,但第一和第三会失败,我想知道。这要求我尝试所有 5 次保存,如果一次失败,则将整个事情回滚,但只有在所有 5 次都尝试过之后。

不过,我看到的是,在第一个失败的事务之后,所有后续使用的 TransactionScope 都会立即抛出它们自己的 TransactionAbortedException 并且不让我尝试保存以查看它是否有效。

这是一个例子:

using (var scope = new System.Transactions.TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = System.Transactions.IsolationLevel.RepeatableRead}, EnterpriseServicesInteropOption.Full))
{
var outputStatus = new List<string>();

for (int i = 0 ; i < 5 ; i++)
{
try
{
using (var innerScope = new System.Transactions.TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = System.Transactions.IsolationLevel.RepeatableRead}, EnterpriseServicesInteropOption.Full))
{
// Do work here that causes an exception on first iteration only
if (i == 0)
{
throw new Exception(string.Format("Iteration {0} has FAILED", i));
}
else
{
outputStatus.Add("SUCCESS");
}
}
}
catch (Exception e)
{
outputStatus.Add("ERROR, " + e.Message);
}
}

// Print out outputStatus values here
}

在此代码的末尾,outputStatus 集合如下所示:

  • 错误,迭代 0 失败
  • 错误,交易已中止。
  • 错误,交易已中止。
  • 错误,交易已中止。
  • 错误,交易已中止。

在第一个异常之后,其余的都无法到达成功语句。

有没有办法在外部事务范围内运行所有内部事务并允许我控制外部事务范围的回滚?

更新:

在这个示例模拟的真实代码中,我无法更改包含内部 TransactionScope 的代码。它在我无法控制的对象中。因此,我正在寻找的解决方案需要能够处理内部事务抛出的异常。

最佳答案

在尝试自己模拟之后,我发现您实际上无法通过这种方式或按照我最初提出的建议进行操作。如果您以任何方式关联事务范围并且其中之一没有正确完成,则对构造函数的后续调用只会导致异常并中止。如果您尝试手动更改它们或嵌套它们而不关联它们,则在完成时 Dispose()。将抛出一个异常,说明您嵌套不正确或 Transaction.Current 在范围内发生了变化。

在我看来,您必须在进行原子 事务和独立尝试所有这些事务之间做出选择,然后检查失败的事务并在之后进行更正。

最后我发现(通过使用 JetBrains dotPeek)事务具有线程关联性。您可以通过在不同的线程上执行 5 个调用来管理它们。当然,您将不得不使用某种屏障 http://en.wikipedia.org/wiki/Synchronous_rendezvous , 以防止任何线程在所有线程完成之前完成。如果它们是顺序的,您将不得不使用额外的同步结构让它们按顺序执行。

请记住,这不是原子的,在您决定要完成所有事务后,它们可能仍然会出错!他们毕竟是独立的。如果您不小心,您可能会被锁定,这取决于您的实际工作应该做什么。此外,如果您的资源分散在不同的机器或数据库中,这可能不会很好地发挥作用,这会增加您的应用程序发出完整但远程资源另有决定的可能性。

原答案:

您应该在内部(循环)结束之前捕获您的异常。

继续阅读(备注):http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope.aspx

If no exception occurs within the transaction scope (that is, between the initialization of the TransactionScope object and the calling of its Dispose method), then the transaction in which the scope participates is allowed to proceed. If an exception does occur within the transaction scope, the transaction in which it participates will be rolled back.

我建议您也阅读这篇文章:http://msdn.microsoft.com/en-us/library/ms172152.aspx

Suppress is useful when you want to preserve the operations performed by the code section, and do not want to abort the ambient transaction if the operations fail. For example, when you want to perform logging or audit operations, or when you want to publish events to subscribers regardless of whether your ambient transaction commits or aborts. This value allows you to have a non-transactional code section inside a transaction scope, as shown in the following example.

补充:我一直在阅读 msdn,我认为如果您创建另一个级别的事务,您可能能够做到这一点。我的推理是:

  • 您的事务失败,因为您控制的范围(最外层)是根事务。
  • 您无法控制的代码确实要求交易但不要求新交易(Argument TransactionScopeOption.Required.)这意味着在该库内部 outer 是正在运行的交易并且失败其他一切都会失败。
  • 为防止这种情况发生,您可以在失去对外部代码的控制之前创建另一个作用域。但请确保您要求一个 RequiredNew 范围。这将隔离您无法控制的代码,并让您有机会捕获该异常。

我修改后的解决方案是这样的。

  using (var scope = new System.Transactions.TransactionScope(TransactionScopeOption.Required))
{
var outputStatus = new List<string>();

for (int i = 0 ; i < 5 ; i++)
{
//Note RequiredNew, rest of the arguments suppressed
using (var innerScope = new System.Transactions.TransactionScope(TransactionScopeOption.RequiredNew))
{
try
{

// Do work here that causes an exception on first iteration only <-- is this really the case or is just an example, if so could you skip the first one?
SomeService.DoSOmetaskWhichUsesATransactionInsideOfIt(i);
outputStatus.Add("SUCCESS : " + i );
innerScope.Complete();

}
catch (Exception e)
{
outputStatus.Add("ERROR, " + i + " " + e.Message);
}
}

}
// IN here you must inspect outputStatus and decide if you want to complete the transaction (all of it , or the parts that didn't fail) or not.
if(/* all good */) {
scope.Complete();
}
// Print out outputStatus values here
}

如果这不适合您的需求,您可能需要研究更高级的事务主题并明确执行。我建议您阅读:http://msdn.microsoft.com/en-us/library/ms172146.aspx这超出了我对交易的理解,所以我不太确定你会如何应用它。

关于c# - 内部 TransactionScope 中的异常导致所有后续内部 TransactionScopes 抛出 TransactionAbortedException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19322310/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com