Search code examples
c#multithreadingtransactionstransactionscope

Determining the status of Transaction after disposal


I am trying to determine if the transaction has been completed or not, and depending on the results, I want to do other stuff in another thread.

Consider the below TransactionScope:

using (TransactionScope scope = new TransactionScope())
{
    // Do stuff

    Scope.Complete();
}

Now, in another class, in a separate thread, I am going through a list of objects that have similar transactions:

private static void ProcessActions()
{
    while(true)
    {
        action = pendingActions[0];
        if (action.CurrentTransaction == null || 
            action.CurrentTransaction.TransactionInformation.Status == TransactionStatus.Committed)
        {
            // Proceed to do things!!
            remove = true;
        }
        else if (action.CurrentTransaction.TransactionInformation.Status == TransactionStatus.Aborted)
        {
            // Transaction has aborted. Remove this action from the list
            remove = true;
        }

        if (remove)
        {
            lock (pendingActions)
            {
                pendingActions.Remove(action);
                eventCount = pendingActions.Count;
            }
        }
    }
}

I set the CurrentTransaction for an action when I am creating a new action in the constructor:

public Action()
{
    CurrentTransaction = System.Transactions.Transaction.Current;
}

The problem is that by the time the other thread is processing the actions, action.CurrentTransaction is disposed, throwing System.ObjectDisposedException.

How can I keep track of the status for each transaction in an Action before it gets disposed?


Solution

  • I believe I have found a solution by utilizing Transaction.TransactionCompleted Event.

    I used the below code to assign a delegate to the TransactionCompleted event:

    System.Transactions.Transaction.Current.TransactionCompleted += new TransactionCompletedEventHandler(Mother.Current_TransactionCompleted);
    

    In that method, I was able to iterate through my actions and determine which one has the corresponding origin Transaction. Like this:

    public static void Current_TransactionCompleted(object sender, TransactionEventArgs e)
    {
        var originatingTransaction = sender as System.Transactions.Transaction;
    
        lock (pendingActions)
        {
            for (int i = pendingActions.Count - 1; i >= 0; i--)
            {
                var action = pendingActions[i];
    
                if (originatingTransaction.Equals(action.CurrentTransaction))
                {
                    var transactionStatus = e.Transaction.TransactionInformation.Status;
                    if (transactionStatus == TransactionStatus.Committed)
                    {
                        // Later in the code, I will do stuff if CurrentTransaction is null
                        action.CurrentTransaction = null;
                    }
                    else if (transactionStatus == TransactionStatus.Aborted)
                    {
                        // if It's aborted, I will remove this action
                        pendingActions.RemoveAt(i);
                    }
                    // I will skip processing any actions that still have a Transaction with the status of "Processing"
                }
            }
        }
    }