Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
956 views
in Technique[技术] by (71.8m points)

c# - TransactionScope error in ambient transaction does not rollback the transaction

I use an ambient transaction like this :


using(TransactionScope tran = new TransactionScope()) {
    CallAMethod1();//INSERT
    CallAMethod2();//INSERT
    tran.Complete();
}

The method CallAMethod2(); returns affected rows =-264 So it fails to insert however the first Insert has been committed !

I want to know how to work with ambient transaction and what if the second method has more than one action which needs internal transaction , should i put these actions in internal transaction ? like this :

     DAL_Helper.Begin_Transaction();

              //------Fill newKeysDictioanry

                affectedRow = DBUtilities.InsertEntityWithTrans("table2", newKeysDictioanry, DAL_Helper);

                if (affectedRow == 1)
                {
                    if (!string.IsNullOrEmpty(sp_confirm))
                    {
                        result_dt = UserTransactionDAL.Run_PostConfirm_SP(sp_PostConfirm, OBJ.ValuesKey, DAL_Helper);
                        if (result_dt.Rows.Count > 0 && result_dt.Rows[0][0].ToString() == "0")
                        {
                            DAL_Helper.current_trans.Commit();

                            if (DAL_Helper.connectionState == ConnectionState.Open)
                            {
                                DAL_Helper.Close_Connection();
                            }
                            return 1;// affectedRow;
                        }
                        else
                        {
                            DAL_Helper.current_trans.Rollback();
                            if (DAL_Helper.connectionState == ConnectionState.Open)
                            {
                                DAL_Helper.Close_Connection();
                            }
                            return -2; 
                        }
                    }
//etc
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

1) You need to check whether the tran.Complete(); is called. If the tran.Complete(); is called, the TransactionScope is considered completed successfully.

From MSDN

When your application completes all work it wants to perform in a transaction, you should call the Complete method only once to inform that transaction manager that it is acceptable to commit the transaction. Failing to call this method aborts the transaction.

The call to tran.Complete(); is to inform the Transaction Manager to complete the transaction. Actually, the Transaction Manager does not track your Db adapter and does not know if an operation in a connection was successful or failed. Your application has to let it know by calling Complete

How does TransactionScope roll back transactions?

To fail your transaction, just ensure that you don't call tran.Complete(); in your code:

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.

In your case, maybe you can throw an exception in your CallAMethod2(); if you think the operation has failed so the tran.Complete(); is not called and the transaction is rolled back.

2) The second thing you can check is whether your connection is enlisted in the transaction. TransactionScope does not rollback if the connection is not enlisted. The possible problems are:

In these cases, you can try enlisting your connection manually (extracted from the link above):

connection.EnlistTransaction(Transaction.Current)

Regarding your second question:

what if the second method has more than one action which need internal transaction , should i put these actions in internal transaction ?

I would say it really depends on whether you consider your CallAMethod2(); as an automic operation which means you can call it elsewhere directly without wrapping it inside a transaction. Most of the cases, it would make sense to create internal transactions as transactions can be nested. In your case, it's recommended to also use TransactionScope in your CallAMethod2();, we have some options when creating a new transaction scope:

The TransactionScope class provides several overloaded constructors that accept an enumeration of the type TransactionScopeOption, which defines the transactional behavior of the scope. A TransactionScope object has three options:

Join the ambient transaction, or create a new one if one does not exist.

Be a new root scope, that is, start a new transaction and have that transaction be the new ambient transaction inside its own scope.

Not take part in a transaction at all. There is no ambient transaction as a result.

Which one to choose really depends on your application. In your case, I guess you could go with the first option. Below is an example from MSDN

void RootMethod()
{
     using(TransactionScope scope = new TransactionScope())
     {
          /* Perform transactional work here */
          SomeMethod();
          scope.Complete();
     }
}

void SomeMethod()
{
     using(TransactionScope scope = new TransactionScope())
     {
          /* Perform transactional work here */
          scope.Complete();
     }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...