3

我正在尝试制作一个快速的虚拟应用程序,以便了解 System.Transactions 的来龙去脉。这个应用程序与 2 个不同的 SQLExpress DB 交互。如果我在组件服务中提取我的事务统计信息,我可以看到在打开第二个连接时在 outerScope 中启动了一个事务。如果 failOuter 为真,则事务中止,但不会引发任何异常。当 failInner 为真时,会抛出 TransactionAbortedException。

来自 MSDN:

当您的应用程序完成它想要在事务中执行的所有工作时,您应该只调用一次 Complete 方法来通知事务管理器可以接受提交事务。将 Complete 调用作为 using 块中的最后一条语句是非常好的做法。

未能调用此方法会中止事务,因为事务管理器将此解释为系统故障,或等效于事务范围内抛出的异常。

如果作用域创建事务并且事务被中止,则会引发 TransactionAbortedException。

基于此,我希望我的 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;
    }
4

1 回答 1

7

通常,在 TransactionScope 超出范围并被处置之前,您不会因未能调用 TransactionScope.Complete() 而引发异常。事务将悄悄地回滚。

您的案例中的异常正在发生,因为您试图在外部 TransactionScope 上调用 Complete 并且由于内部 TransactionScope 已经失败而无法正确完成 - 因此这会引发异常。

那有意义吗?

如果您想在外部事务中止的情况下执行某些操作,您可以尝试以下操作:

// 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
}

尽管我认为更简洁的通用模式是始终在 TransactionScope 内调用 Complete() 并处理任何导致的异常,如果您想针对事务失败执行特定操作。

于 2009-04-24T18:38:19.017 回答