File: System\ServiceModel\Activities\Dispatcher\TransactionContext.cs
Project: ndp\cdf\src\NetFx40\System.ServiceModel.Activities\System.ServiceModel.Activities.csproj (System.ServiceModel.Activities)
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//----------------------------------------------------------------
 
namespace System.ServiceModel.Activities.Dispatcher
{
    using System.Runtime;
    using System.Transactions;
 
    //1) On Tx.Prepare
    //    Persist the instance.
    //          When Persist completes Tx.Prepared called.  
    //          When Persist fails Tx.ForceRollback called.
    //2) On Tx.Commit
    //     DurableInstance.OnTransactionCompleted().
    //3) On Tx.Abort
    //     DurableInstance.OnTransactionAborted()
    class TransactionContext : IEnlistmentNotification
    {
        static AsyncCallback handleEndPrepare = Fx.ThunkCallback(new AsyncCallback(HandleEndPrepare));
        Transaction currentTransaction;
        WorkflowServiceInstance durableInstance;
 
        public TransactionContext(WorkflowServiceInstance durableInstance, Transaction currentTransaction)
        {
            Fx.Assert(durableInstance != null, "Null DurableInstance passed to TransactionContext.");
            Fx.Assert(currentTransaction != null, "Null Transaction passed to TransactionContext.");
 
            this.currentTransaction = currentTransaction.Clone();
            this.durableInstance = durableInstance;
            this.currentTransaction.EnlistVolatile(this, EnlistmentOptions.EnlistDuringPrepareRequired);
        }
 
        public Transaction CurrentTransaction
        {
            get
            {
                return this.currentTransaction;
            }
        }
 
        void IEnlistmentNotification.Commit(Enlistment enlistment)
        {
            enlistment.Done();
            this.durableInstance.TransactionCommitted();
        }
 
        void IEnlistmentNotification.InDoubt(Enlistment enlistment)
        {
            enlistment.Done();
            Fx.Assert(this.currentTransaction.TransactionInformation.Status == TransactionStatus.InDoubt, "Transaction state should be InDoubt at this point");
            TransactionException exception = this.GetAbortedOrInDoubtTransactionException();
 
            Fx.Assert(exception != null, "Need a valid TransactionException at this point");
            this.durableInstance.OnTransactionAbortOrInDoubt(exception);
        }
 
        void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment)
        {
            bool success = false;
            try
            {
                IAsyncResult result = new PrepareAsyncResult(this, TransactionContext.handleEndPrepare, preparingEnlistment);
                if (result.CompletedSynchronously)
                {
                    PrepareAsyncResult.End(result);
                    preparingEnlistment.Prepared();
                }
                success = true;
            }
            //we need to swollow the TransactionException as it could because another party aborting it
            catch (TransactionException) 
            {
            }
            finally
            {
                if (!success)
                {
                    preparingEnlistment.ForceRollback();
                }
            }
        }
 
        void IEnlistmentNotification.Rollback(Enlistment enlistment)
        {
            enlistment.Done();
            Fx.Assert(this.currentTransaction.TransactionInformation.Status == TransactionStatus.Aborted, "Transaction state should be Aborted at this point");
            TransactionException exception = this.GetAbortedOrInDoubtTransactionException();
 
            Fx.Assert(exception != null, "Need a valid TransactionException at this point");
            this.durableInstance.OnTransactionAbortOrInDoubt(exception);
        }
 
        TransactionException GetAbortedOrInDoubtTransactionException()
        {
            try
            {
                TransactionHelper.ThrowIfTransactionAbortedOrInDoubt(this.currentTransaction);
            }
            catch (TransactionException exception)
            {
                return exception;
            }
            return null;
        }
 
        static void HandleEndPrepare(IAsyncResult result)
        {
            PreparingEnlistment preparingEnlistment = (PreparingEnlistment)result.AsyncState;
            bool success = false;
            try
            {
                if (!result.CompletedSynchronously)
                {
                    PrepareAsyncResult.End(result);
                    preparingEnlistment.Prepared();
                }
                success = true;
            }
            //we need to swollow the TransactionException as it could because another party aborting it
            catch (TransactionException) 
            {
            }
            finally
            {
                if (!success)
                {
                    preparingEnlistment.ForceRollback();
                }
            }
        }
 
        class PrepareAsyncResult : TransactedAsyncResult
        {
            static readonly AsyncCompletion onEndPersist = new AsyncCompletion(OnEndPersist);
 
            readonly TransactionContext context;
 
            public PrepareAsyncResult(TransactionContext context, AsyncCallback callback, object state)
                : base(callback, state)
            {
                this.context = context;
 
                IAsyncResult result = null;
                using (PrepareTransactionalCall(this.context.currentTransaction))
                {
                    result = this.context.durableInstance.BeginPersist(TimeSpan.MaxValue, PrepareAsyncCompletion(PrepareAsyncResult.onEndPersist), this);
                }
                if (SyncContinue(result))
                {
                    Complete(true);
                }
            }
 
            public static void End(IAsyncResult result)
            {
                AsyncResult.End<PrepareAsyncResult>(result);
            }
 
            static bool OnEndPersist(IAsyncResult result)
            {
                PrepareAsyncResult thisPtr = (PrepareAsyncResult)result.AsyncState;
                thisPtr.context.durableInstance.EndPersist(result);
                thisPtr.context.durableInstance.OnTransactionPrepared();
                return true;
            }
        }
    }
}