C# 试图理解 TransactionScope
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/787052/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Trying to understand TransactionScope
提问by Justin Holbrook
I'm trying to make a quick dummy app so I can learn the ins and outs of System.Transactions. This app interacts with 2 different SQLExpress DBs. If I pull up my transaction statistics in component services, I can see a transaction start up in the outerScope when the second connection is opened. If failOuter is true the transaction aborts, but it doesn't throw any exceptions. When failInner is true a TransactionAbortedException is thrown.
我正在尝试制作一个快速的虚拟应用程序,以便我可以了解 System.Transactions 的来龙去脉。此应用程序与 2 个不同的 SQLExpress 数据库交互。如果我在组件服务中调出我的事务统计信息,我可以看到在打开第二个连接时,outerScope 中启动了一个事务。如果 failOuter 为真,则事务中止,但不会引发任何异常。当 failInner 为 true 时,会抛出 TransactionAbortedException。
From MSDN:
When your application completes all the work it wants to perform in a transaction, you should call the Complete method only once to inform the transaction manager that it is acceptable to commit the transaction. It is very good practice to put the call to Complete as the last statement in the using block.
Failing to call this method aborts the transaction, because the transaction manager interprets this as a system failure, or equivalent to an exception thrown within the scope of transaction.
A TransactionAbortedException is thrown if the scope creates the transaction, and the transaction is aborted.
来自 MSDN:
当您的应用程序完成它想要在事务中执行的所有工作时,您应该只调用 Complete 方一次以通知事务管理器可以提交事务。将 Complete 调用作为 using 块中的最后一条语句是非常好的做。
未能调用此方会中止事务,因为事务管理器将此解释为系统故障,或者相当于事务范围内抛出的异常。
如果作用域创建事务并且事务被中止,则抛出 TransactionAbortedException。
Based on that I would expect my outerScope to throw a TransactionAbortedException since my Transaction Statistics are showing an aborted transaction everytime I run my app with failOuter set to true. My method returns a true since no exceptions are thrown even if the transaction aborts. Unless I abort the inner transaction, then it behaves as I would expect. Any clarification would be most appreciated.
基于此,我希望我的 outerScope 抛出 TransactionAbortedException,因为每次我在将 failOuter 设置为 true 的情况下运行我的应用程序时,我的事务统计信息都会显示一个中止的事务。我的方返回 true,因为即使事务中止也不会抛出异常。除非我中止内部事务,否则它会按照我的预期运行。任何澄清将不胜感激。
public bool CreateNestedTransaction(bool failOuter, bool failInner)
{
try
{
using (TransactionScope outerScope = new TransactionScope())
{
/* Perform transactional work here */
using (SqlConnection myConnection = new SqlConnection("server=(local)\SQLExpress;Integrated Security=SSPI;database=test1"))
{
SqlCommand myCommand = new SqlCommand();
myConnection.Open();
myCommand.Connection = myConnection;
myCommand.CommandText = "update test set Value = ((select Value from test where Id = (select max(Id) from test))+1) where Id = (select max(Id) from test)";
myCommand.ExecuteNonQuery();
}
using (SqlConnection myConnection = new SqlConnection("server=(local)\SQLExpress;Integrated Security=SSPI;database=test1"))
{
SqlCommand myCommand = new SqlCommand();
myConnection.Open();
myCommand.Connection = myConnection;
myCommand.CommandText = "update test set Value = Value";
myCommand.ExecuteNonQuery();
}
using (TransactionScope innerScope = new TransactionScope())
{
using (SqlConnection myConnection = new SqlConnection("server=(local)\SQLExpress;Integrated Security=SSPI;database=test2"))
{
SqlCommand myCommand = new SqlCommand();
myConnection.Open();
myCommand.Connection = myConnection;
myCommand.CommandText = "update test set Value = ((select Value from test where Id = (select max(Id) from test))+1) where Id = (select max(Id) from test)";
myCommand.ExecuteNonQuery();
}
if (failInner == false) { innerScope.Complete(); }
}
if (failOuter == false) { outerScope.Complete(); }
}
}
catch (TransactionAbortedException)
{
return false;
}
return true;
}
采纳答案by Steve Willcock
Normally you don't get an exception thrown by failing to call TransactionScope.Complete() before the TransactionScope goes out of scope and gets disposed. The transaction will just quietly roll back.
通常,在 TransactionScope 超出范围并被处置之前,您不会因未能调用 TransactionScope.Complete() 而引发异常。事务只会悄悄地回滚。
The exception in your case is happening because you are trying to call Complete on the outer TransactionScope and it can't complete properly because the inner TransactionScope already failed - hence this throws an exception.
您的情况发生异常是因为您试图在外部 TransactionScope 上调用 Complete 并且它无正确完成,因为内部 TransactionScope 已经失败 - 因此这会引发异常。
Does that make sense?
那有意义吗?
If you want to carry out some operation if your outer transaction aborted you might try something like this:
如果您想在外部事务中止时执行某些操作,您可以尝试以下操作:
// Inside each using TransactionScope(), hhok up the current transaction completed event
Transaction.Current.TransactionCompleted += new TransactionCompletedEventHandler(Current_TransactionCompleted);
// handle the event somewhere else
void Current_TransactionCompleted(object sender, TransactionEventArgs e)
{
// check the status of the transaction
if(e.Transaction.TransactionInformation.Status == TransactionStatus.Aborted)
// do something here
}
Although I think a cleaner pattern for general usage would be to always call Complete() inside your TransactionScope and handle any resulting exception if you want to do something specific on transaction failure.
尽管我认为一般使用的更清晰模式是始终在 TransactionScope 中调用 Complete() 并处理任何结果异常,如果您想对事务失败执行特定操作。