Trying to understand TransactionScope

I am trying to make a quick dummy application so that I can learn all aspects of System.Transactions. This application interacts with two different SQLExpress DB. If I pull out the transaction statistics in the component services, I can see the transaction starting in the external Scope when the second connection opens. If failOuter is true, the transaction is aborted, but it does not throw any exceptions. When failInner is true, a TransactionAbortedException is thrown.

From MSDN:

When your application completes all the work that it wants to perform in a transaction, you must call the Complete method only once to tell the transaction manager that it is acceptable to complete the transaction. It is very good practice to call Complete as the last statement in the use block.

Failure to follow this method aborts the transaction because the transaction manager interprets this as a system failure or is equivalent to an exception that is part of the transaction volume.

A TransactionAbortedException is thrown if the scope creates a transaction and the transaction is aborted.

Based on this, I would expect my outerScope to throw a TransactionAbortedException, since my transaction statistics show a choppy transaction every time I launch my application with the failOuter parameter equal to true. My method returns true, since no exceptions are thrown, even if the transaction is aborted. If I do not cancel the internal transaction, then it behaves as I expected. Any clarification would be most appreciated.

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;
    }
+3
source share
1 answer

Usually you do not get an exception caused by the inability to call TransactionScope.Complete () before the TransactionScope goes out of scope and gets access. The transaction will simply be rolled back.

, Complete TransactionScope, , 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
}

, Complete() TransactionScope , - .

+7

Source: https://habr.com/ru/post/1707072/


All Articles