1 //---------------------------------------------------------------- 2 // Copyright (c) Microsoft Corporation. All rights reserved. 3 //---------------------------------------------------------------- 4 5 namespace System.ServiceModel.Activities.Dispatcher 6 { 7 using System.Runtime; 8 using System.Transactions; 9 10 //1) On Tx.Prepare 11 // Persist the instance. 12 // When Persist completes Tx.Prepared called. 13 // When Persist fails Tx.ForceRollback called. 14 //2) On Tx.Commit 15 // DurableInstance.OnTransactionCompleted(). 16 //3) On Tx.Abort 17 // DurableInstance.OnTransactionAborted() 18 class TransactionContext : IEnlistmentNotification 19 { 20 static AsyncCallback handleEndPrepare = Fx.ThunkCallback(new AsyncCallback(HandleEndPrepare)); 21 Transaction currentTransaction; 22 WorkflowServiceInstance durableInstance; 23 TransactionContext(WorkflowServiceInstance durableInstance, Transaction currentTransaction)24 public TransactionContext(WorkflowServiceInstance durableInstance, Transaction currentTransaction) 25 { 26 Fx.Assert(durableInstance != null, "Null DurableInstance passed to TransactionContext."); 27 Fx.Assert(currentTransaction != null, "Null Transaction passed to TransactionContext."); 28 29 this.currentTransaction = currentTransaction.Clone(); 30 this.durableInstance = durableInstance; 31 this.currentTransaction.EnlistVolatile(this, EnlistmentOptions.EnlistDuringPrepareRequired); 32 } 33 34 public Transaction CurrentTransaction 35 { 36 get 37 { 38 return this.currentTransaction; 39 } 40 } 41 IEnlistmentNotification.Commit(Enlistment enlistment)42 void IEnlistmentNotification.Commit(Enlistment enlistment) 43 { 44 enlistment.Done(); 45 this.durableInstance.TransactionCommitted(); 46 } 47 IEnlistmentNotification.InDoubt(Enlistment enlistment)48 void IEnlistmentNotification.InDoubt(Enlistment enlistment) 49 { 50 enlistment.Done(); 51 Fx.Assert(this.currentTransaction.TransactionInformation.Status == TransactionStatus.InDoubt, "Transaction state should be InDoubt at this point"); 52 TransactionException exception = this.GetAbortedOrInDoubtTransactionException(); 53 54 Fx.Assert(exception != null, "Need a valid TransactionException at this point"); 55 this.durableInstance.OnTransactionAbortOrInDoubt(exception); 56 } 57 IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment)58 void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment) 59 { 60 bool success = false; 61 try 62 { 63 IAsyncResult result = new PrepareAsyncResult(this, TransactionContext.handleEndPrepare, preparingEnlistment); 64 if (result.CompletedSynchronously) 65 { 66 PrepareAsyncResult.End(result); 67 preparingEnlistment.Prepared(); 68 } 69 success = true; 70 } 71 //we need to swollow the TransactionException as it could because another party aborting it 72 catch (TransactionException) 73 { 74 } 75 finally 76 { 77 if (!success) 78 { 79 preparingEnlistment.ForceRollback(); 80 } 81 } 82 } 83 IEnlistmentNotification.Rollback(Enlistment enlistment)84 void IEnlistmentNotification.Rollback(Enlistment enlistment) 85 { 86 enlistment.Done(); 87 Fx.Assert(this.currentTransaction.TransactionInformation.Status == TransactionStatus.Aborted, "Transaction state should be Aborted at this point"); 88 TransactionException exception = this.GetAbortedOrInDoubtTransactionException(); 89 90 Fx.Assert(exception != null, "Need a valid TransactionException at this point"); 91 this.durableInstance.OnTransactionAbortOrInDoubt(exception); 92 } 93 GetAbortedOrInDoubtTransactionException()94 TransactionException GetAbortedOrInDoubtTransactionException() 95 { 96 try 97 { 98 TransactionHelper.ThrowIfTransactionAbortedOrInDoubt(this.currentTransaction); 99 } 100 catch (TransactionException exception) 101 { 102 return exception; 103 } 104 return null; 105 } 106 HandleEndPrepare(IAsyncResult result)107 static void HandleEndPrepare(IAsyncResult result) 108 { 109 PreparingEnlistment preparingEnlistment = (PreparingEnlistment)result.AsyncState; 110 bool success = false; 111 try 112 { 113 if (!result.CompletedSynchronously) 114 { 115 PrepareAsyncResult.End(result); 116 preparingEnlistment.Prepared(); 117 } 118 success = true; 119 } 120 //we need to swollow the TransactionException as it could because another party aborting it 121 catch (TransactionException) 122 { 123 } 124 finally 125 { 126 if (!success) 127 { 128 preparingEnlistment.ForceRollback(); 129 } 130 } 131 } 132 133 class PrepareAsyncResult : TransactedAsyncResult 134 { 135 static readonly AsyncCompletion onEndPersist = new AsyncCompletion(OnEndPersist); 136 137 readonly TransactionContext context; 138 PrepareAsyncResult(TransactionContext context, AsyncCallback callback, object state)139 public PrepareAsyncResult(TransactionContext context, AsyncCallback callback, object state) 140 : base(callback, state) 141 { 142 this.context = context; 143 144 IAsyncResult result = null; 145 using (PrepareTransactionalCall(this.context.currentTransaction)) 146 { 147 result = this.context.durableInstance.BeginPersist(TimeSpan.MaxValue, PrepareAsyncCompletion(PrepareAsyncResult.onEndPersist), this); 148 } 149 if (SyncContinue(result)) 150 { 151 Complete(true); 152 } 153 } 154 End(IAsyncResult result)155 public static void End(IAsyncResult result) 156 { 157 AsyncResult.End<PrepareAsyncResult>(result); 158 } 159 OnEndPersist(IAsyncResult result)160 static bool OnEndPersist(IAsyncResult result) 161 { 162 PrepareAsyncResult thisPtr = (PrepareAsyncResult)result.AsyncState; 163 thisPtr.context.durableInstance.EndPersist(result); 164 thisPtr.context.durableInstance.OnTransactionPrepared(); 165 return true; 166 } 167 } 168 } 169 } 170