1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
//----------------------------------------------------------------
// 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;
}
}
}
}
|