试图了解TransactionScope

Jus*_*ook 3 c# transactions transactionscope

我正在尝试制作一个快速的虚拟应用程序,以便我可以了解System.Transactions的细节.此应用程序与2个不同的SQLExpress DB交互.如果我在组件服务中提取事务统计信息,我可以在打开第二个连接时看到在outerScope中启动事务.如果failOuter为true,则事务将中止,但不会抛出任何异常.当failInner为true时,抛出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;
    }
Run Code Online (Sandbox Code Playgroud)

Ste*_*ock 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
}
Run Code Online (Sandbox Code Playgroud)

虽然我认为一般用法的清晰模式总是在TransactionScope中调用Complete()并处理任何结果异常,如果你想在事务失败时做一些特定的事情.