| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073 |
- //-----------------------------------------------------------------------------
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //-----------------------------------------------------------------------------
- namespace System.ServiceModel.Dispatcher
- {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Globalization;
- using System.Runtime;
- using System.Runtime.CompilerServices;
- using System.ServiceModel;
- using System.ServiceModel.Activation;
- using System.ServiceModel.Channels;
- using System.ServiceModel.Diagnostics;
- using System.ServiceModel.Diagnostics.Application;
- using System.Transactions;
- class TransactionBehavior
- {
- bool isConcurrent;
- IsolationLevel isolation = ServiceBehaviorAttribute.DefaultIsolationLevel;
- DispatchRuntime dispatch;
- TimeSpan timeout = TimeSpan.Zero;
- bool isTransactedReceiveChannelDispatcher = false;
- internal TransactionBehavior()
- {
- }
- internal TransactionBehavior(DispatchRuntime dispatch)
- {
- this.isConcurrent = (dispatch.ConcurrencyMode == ConcurrencyMode.Multiple ||
- dispatch.ConcurrencyMode == ConcurrencyMode.Reentrant);
- this.dispatch = dispatch;
- this.isTransactedReceiveChannelDispatcher = dispatch.ChannelDispatcher.IsTransactedReceive;
- // Don't pull in System.Transactions.dll if we don't need it
- if (dispatch.ChannelDispatcher.TransactionIsolationLevelSet)
- {
- this.InitializeIsolationLevel(dispatch);
- }
- this.timeout = TransactionBehavior.NormalizeTimeout(dispatch.ChannelDispatcher.TransactionTimeout);
- }
- internal static Exception CreateFault(string reasonText, string codeString, bool isNetDispatcherFault)
- {
- string faultCodeNamespace, action;
- // 'Transactions' action should be used only when we expect to have a TransactionChannel in the channel stack
- // otherwise one should use the NetDispatch action.
- if (isNetDispatcherFault)
- {
- faultCodeNamespace = FaultCodeConstants.Namespaces.NetDispatch;
- action = FaultCodeConstants.Actions.NetDispatcher;
- }
- else
- {
- faultCodeNamespace = FaultCodeConstants.Namespaces.Transactions;
- action = FaultCodeConstants.Actions.Transactions;
- }
- FaultReason reason = new FaultReason(reasonText, CultureInfo.CurrentCulture);
- FaultCode code = FaultCode.CreateSenderFaultCode(codeString, faultCodeNamespace);
- return new FaultException(reason, code, action);
- }
- internal static TransactionBehavior CreateIfNeeded(DispatchRuntime dispatch)
- {
- if (TransactionBehavior.NeedsTransactionBehavior(dispatch))
- {
- return new TransactionBehavior(dispatch);
- }
- else
- {
- return null;
- }
- }
- internal static TimeSpan NormalizeTimeout(TimeSpan timeout)
- {
- if (TimeSpan.Zero == timeout)
- {
- timeout = TransactionManager.DefaultTimeout;
- }
- else if (TimeSpan.Zero != TransactionManager.MaximumTimeout && timeout > TransactionManager.MaximumTimeout)
- {
- timeout = TransactionManager.MaximumTimeout;
- }
- return timeout;
- }
- internal static CommittableTransaction CreateTransaction(IsolationLevel isolation, TimeSpan timeout)
- {
- TransactionOptions options = new TransactionOptions();
- options.IsolationLevel = isolation;
- options.Timeout = timeout;
- return new CommittableTransaction(options);
- }
- internal void SetCurrent(ref MessageRpc rpc)
- {
- if (!this.isConcurrent)
- {
- rpc.InstanceContext.Transaction.SetCurrent(ref rpc);
- }
- }
- internal void ResolveOutcome(ref MessageRpc rpc)
- {
- if ((rpc.InstanceContext != null) && (rpc.transaction != null))
- {
- TransactionInstanceContextFacet context = rpc.InstanceContext.Transaction;
- if (context != null)
- {
- context.CheckIfTxCompletedAndUpdateAttached(ref rpc, this.isConcurrent);
- }
- rpc.Transaction.Complete(rpc.Error);
- }
- }
- Transaction GetInstanceContextTransaction(ref MessageRpc rpc)
- {
- return rpc.InstanceContext.Transaction.Attached;
- }
- [MethodImpl(MethodImplOptions.NoInlining)]
- void InitializeIsolationLevel(DispatchRuntime dispatch)
- {
- this.isolation = dispatch.ChannelDispatcher.TransactionIsolationLevel;
- }
- static bool NeedsTransactionBehavior(DispatchRuntime dispatch)
- {
- DispatchOperation unhandled = dispatch.UnhandledDispatchOperation;
- if ((unhandled != null) && (unhandled.TransactionRequired))
- {
- return true;
- }
- if (dispatch.ChannelDispatcher.IsTransactedReceive) //check if we have transacted receive
- {
- return true;
- }
- for (int i = 0; i < dispatch.Operations.Count; i++)
- {
- DispatchOperation operation = dispatch.Operations[i];
- if (operation.TransactionRequired)
- {
- return true;
- }
- }
- return false;
- }
- internal void ResolveTransaction(ref MessageRpc rpc)
- {
- if (rpc.Operation.HasDefaultUnhandledActionInvoker)
- {
- // we ignore unhandled operations
- return;
- }
- Transaction contextTransaction = null;
- //If we are inside a TransactedReceiveScope in workflow, then we need to look into the PPD and not the InstanceContext
- //to get the contextTransaction
- if (rpc.Operation.IsInsideTransactedReceiveScope)
- {
- // We may want to use an existing transaction for the instance.
- IInstanceTransaction instanceTransaction = rpc.Operation.Invoker as IInstanceTransaction;
- if (instanceTransaction != null)
- {
- contextTransaction = instanceTransaction.GetTransactionForInstance(rpc.OperationContext);
- }
- if (contextTransaction != null)
- {
- if (DiagnosticUtility.ShouldTraceInformation)
- {
- TraceUtility.TraceEvent(TraceEventType.Information,
- TraceCode.TxSourceTxScopeRequiredUsingExistingTransaction,
- SR.GetString(SR.TraceCodeTxSourceTxScopeRequiredUsingExistingTransaction,
- contextTransaction.TransactionInformation.LocalIdentifier,
- rpc.Operation.Name)
- );
- }
- }
- }
- else
- {
- contextTransaction = this.GetInstanceContextTransaction(ref rpc);
- }
- Transaction transaction = null;
- try
- {
- transaction = TransactionMessageProperty.TryGetTransaction(rpc.Request);
- }
- catch (TransactionException e)
- {
- DiagnosticUtility.TraceHandledException(e, TraceEventType.Error);
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(TransactionBehavior.CreateFault(SR.GetString(SR.SFxTransactionUnmarshalFailed, e.Message), FaultCodeConstants.Codes.TransactionUnmarshalingFailed, false));
- }
- if (rpc.Operation.TransactionRequired)
- {
- if (transaction != null)
- {
- if (this.isTransactedReceiveChannelDispatcher)
- {
- if (DiagnosticUtility.ShouldTraceInformation)
- {
- TraceUtility.TraceEvent(TraceEventType.Information,
- TraceCode.TxSourceTxScopeRequiredIsTransactedTransport,
- SR.GetString(SR.TraceCodeTxSourceTxScopeRequiredIsTransactedTransport,
- transaction.TransactionInformation.LocalIdentifier,
- rpc.Operation.Name)
- );
- }
- }
- else
- {
- if (DiagnosticUtility.ShouldTraceInformation)
- {
- TraceUtility.TraceEvent(TraceEventType.Information,
- TraceCode.TxSourceTxScopeRequiredIsTransactionFlow,
- SR.GetString(SR.TraceCodeTxSourceTxScopeRequiredIsTransactionFlow,
- transaction.TransactionInformation.LocalIdentifier,
- rpc.Operation.Name)
- );
- }
- if (PerformanceCounters.PerformanceCountersEnabled)
- {
- PerformanceCounters.TxFlowed(PerformanceCounters.GetEndpointDispatcher(), rpc.Operation.Name);
- }
- bool sameTransaction = false;
- if (rpc.Operation.IsInsideTransactedReceiveScope)
- {
- sameTransaction = transaction.Equals(contextTransaction);
- }
- else
- {
- sameTransaction = transaction == contextTransaction;
- }
- if (!sameTransaction)
- {
- try
- {
- transaction = transaction.DependentClone(DependentCloneOption.RollbackIfNotComplete);
- }
- catch (TransactionException e)
- {
- DiagnosticUtility.TraceHandledException(e, TraceEventType.Error);
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(TransactionBehavior.CreateFault(SR.GetString(SR.SFxTransactionAsyncAborted), FaultCodeConstants.Codes.TransactionAborted, true));
- }
- }
- }
- }
- }
- else
- {
- // We got a transaction from the ChannelHandler.
- // Transport is transacted.
- // But operation doesn't require the transaction, so no one ever will commit it.
- // Because of that we have to commit it here.
- if (transaction != null && this.isTransactedReceiveChannelDispatcher)
- {
- try
- {
- if (null != rpc.TransactedBatchContext)
- {
- rpc.TransactedBatchContext.ForceCommit();
- rpc.TransactedBatchContext = null;
- }
- else
- {
- TransactionInstanceContextFacet.Complete(transaction, null);
- }
- }
- finally
- {
- transaction.Dispose();
- transaction = null;
- }
- }
- }
- InstanceContext context = rpc.InstanceContext;
- if (context.Transaction.ShouldReleaseInstance && !this.isConcurrent)
- {
- if (context.Behavior.ReleaseServiceInstanceOnTransactionComplete)
- {
- context.ReleaseServiceInstance();
- if (DiagnosticUtility.ShouldTraceInformation)
- {
- TraceUtility.TraceEvent(TraceEventType.Information,
- TraceCode.TxReleaseServiceInstanceOnCompletion,
- SR.GetString(SR.TraceCodeTxReleaseServiceInstanceOnCompletion,
- contextTransaction.TransactionInformation.LocalIdentifier)
- );
- }
- }
- context.Transaction.ShouldReleaseInstance = false;
- if (transaction == null || transaction == contextTransaction)
- {
- rpc.Transaction.Current = contextTransaction;
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(TransactionBehavior.CreateFault(SR.GetString(SR.SFxTransactionAsyncAborted), FaultCodeConstants.Codes.TransactionAborted, true));
- }
- else
- {
- contextTransaction = null;
- }
- }
- if (rpc.Operation.TransactionRequired)
- {
- if (transaction == null)
- {
- if (contextTransaction != null)
- {
- transaction = contextTransaction;
- if (DiagnosticUtility.ShouldTraceInformation)
- {
- TraceUtility.TraceEvent(TraceEventType.Information,
- TraceCode.TxSourceTxScopeRequiredIsAttachedTransaction,
- SR.GetString(SR.TraceCodeTxSourceTxScopeRequiredIsAttachedTransaction,
- transaction.TransactionInformation.LocalIdentifier,
- rpc.Operation.Name)
- );
- }
- }
- else
- {
- transaction = TransactionBehavior.CreateTransaction(this.isolation, this.timeout);
- if (DiagnosticUtility.ShouldTraceInformation)
- {
- TraceUtility.TraceEvent(TraceEventType.Information,
- TraceCode.TxSourceTxScopeRequiredIsCreateNewTransaction,
- SR.GetString(SR.TraceCodeTxSourceTxScopeRequiredIsCreateNewTransaction,
- transaction.TransactionInformation.LocalIdentifier,
- rpc.Operation.Name)
- );
- }
- }
- }
- if ((this.isolation != IsolationLevel.Unspecified) && (transaction.IsolationLevel != this.isolation))
- {
- throw TraceUtility.ThrowHelperError(TransactionBehavior.CreateFault
- (SR.GetString(SR.IsolationLevelMismatch2, transaction.IsolationLevel, this.isolation), FaultCodeConstants.Codes.TransactionIsolationLevelMismatch, false), rpc.Request);
- }
- rpc.Transaction.Current = transaction;
- rpc.InstanceContext.Transaction.AddReference(ref rpc, rpc.Transaction.Current, true);
- try
- {
- rpc.Transaction.Clone = transaction.Clone();
- if (rpc.Operation.IsInsideTransactedReceiveScope)
- {
- //It is because we want to synchronize the dispatcher processing of messages with the commit
- //processing that is started by the completion of a TransactedReceiveScope. We need to make sure
- //that all the dispatcher processing is done and we can do that by creating a blocking dependent clone and only
- //completing it after all of the message processing is done for a given TransactionRpcFacet
- rpc.Transaction.CreateDependentClone();
- }
- }
- catch (ObjectDisposedException e)//transaction may be async aborted
- {
- DiagnosticUtility.TraceHandledException(e, TraceEventType.Error);
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(TransactionBehavior.CreateFault(SR.GetString(SR.SFxTransactionAsyncAborted), FaultCodeConstants.Codes.TransactionAborted, true));
- }
- rpc.InstanceContext.Transaction.AddReference(ref rpc, rpc.Transaction.Clone, false);
- rpc.OperationContext.TransactionFacet = rpc.Transaction;
- if (!rpc.Operation.TransactionAutoComplete)
- {
- rpc.Transaction.SetIncomplete();
- }
- }
- }
- internal void InitializeCallContext(ref MessageRpc rpc)
- {
- if (rpc.Operation.TransactionRequired)
- {
- rpc.Transaction.ThreadEnter(ref rpc.Error);
- }
- }
- internal void ClearCallContext(ref MessageRpc rpc)
- {
- if (rpc.Operation.TransactionRequired)
- {
- rpc.Transaction.ThreadLeave();
- }
- }
- }
- internal class TransactionRpcFacet
- {
- //internal members
- // Current is the original transaction that we created/flowed/whatever. This is
- // the "current" transaction used by the operation, and we keep it around so we
- // can commit it, complete it, etc.
- //
- // Clone is a clone of Current. We keep it around to pass into TransactionScope
- // so that System.Transactions.Transaction.Current is not CommittableTransaction
- // or anything dangerous like that.
- internal Transaction Current;
- internal Transaction Clone;
- internal DependentTransaction dependentClone;
- internal bool IsCompleted = true;
- internal MessageRpc rpc;
- TransactionScope scope;
- bool transactionSetComplete = false; // To track if user has called SetTransactionComplete()
- internal TransactionRpcFacet()
- {
- }
- internal TransactionRpcFacet(ref MessageRpc rpc)
- {
- this.rpc = rpc;
- }
- // Calling Complete will Commit or Abort the transaction based on,
- // error - If any user error is propagated to the service we abort the transaction unless SetTransactionComplete was successful.
- // transactionDoomed - If internal error occurred and this error may or may not be propagated
- // by the user to the service. Abort the Tx if transactionDoomed is set true.
- //
- // If the user violates the following rules, the transaction is doomed.
- // User cannot call TransactionSetComplete() when TransactionAutoComplete is true.
- // User cannot call TransactionSetComplete() multiple times.
- [MethodImpl(MethodImplOptions.NoInlining)]
- internal void Complete(Exception error)
- {
- if (!object.ReferenceEquals(this.Current, null))
- {
- TransactedBatchContext batchContext = this.rpc.TransactedBatchContext;
- if (null != batchContext)
- {
- if (null == error)
- {
- batchContext.Complete();
- }
- else
- {
- batchContext.ForceRollback();
- }
- batchContext.InDispatch = false;
- }
- else
- {
- if (this.transactionSetComplete)
- {
- // Commit the transaction when TransactionSetComplete() is called and
- // even when an exception(non transactional) happens after this call.
- rpc.InstanceContext.Transaction.CompletePendingTransaction(this.Current, null);
- if (DiagnosticUtility.ShouldTraceInformation)
- {
- TraceUtility.TraceEvent(TraceEventType.Information,
- TraceCode.TxCompletionStatusCompletedForSetComplete,
- SR.GetString(SR.TraceCodeTxCompletionStatusCompletedForSetComplete,
- this.Current.TransactionInformation.LocalIdentifier,
- this.rpc.Operation.Name)
- );
- }
- }
- else if (this.IsCompleted || (error != null))
- {
- rpc.InstanceContext.Transaction.CompletePendingTransaction(this.Current, error);
- }
- }
- if (this.rpc.Operation.IsInsideTransactedReceiveScope)
- {
- //We are done with the message processing associated with this TransactionRpcFacet so a commit that may have
- //been started by a TransactedReceiveScope can move forward.
- this.CompleteDependentClone();
- }
- this.Current = null;
- }
- }
- internal void SetIncomplete()
- {
- this.IsCompleted = false;
- }
- internal void Completed()
- {
- if (this.scope == null)
- {
- return;
- }
- // Prohibit user from calling SetTransactionComplete() when TransactionAutoComplete is set to true.
- // Transaction will be aborted.
- if (this.rpc.Operation.TransactionAutoComplete)
- {
- try
- {
- this.Current.Rollback();
- }
- catch (ObjectDisposedException e)
- {
- //we don't want to mask the real error here
- DiagnosticUtility.TraceHandledException(e, TraceEventType.Error);
- }
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
- SR.GetString(SR.SFxTransactionInvalidSetTransactionComplete, rpc.Operation.Name, rpc.Host.Description.Name)));
- }
- // Prohibit user from calling SetTransactionComplete() multiple times.
- // Transaction will be aborted.
- else if (this.transactionSetComplete)
- {
- try
- {
- this.Current.Rollback();
- }
- catch (ObjectDisposedException e)
- {
- //we don't want to mask the real error here
- DiagnosticUtility.TraceHandledException(e, TraceEventType.Error);
- }
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
- SR.GetString(SR.SFxMultiSetTransactionComplete, rpc.Operation.Name, rpc.Host.Description.Name)));
- }
- this.transactionSetComplete = true;
- this.IsCompleted = true;
- this.scope.Complete();
- }
- [MethodImpl(MethodImplOptions.NoInlining)]
- internal void ThreadEnter(ref Exception error)
- {
- Transaction clone = this.Clone;
- if ((clone != null) && (error == null))
- {
- if (TD.TransactionScopeCreateIsEnabled())
- {
- if (clone != null && clone.TransactionInformation != null)
- {
- TD.TransactionScopeCreate(rpc.EventTraceActivity,
- clone.TransactionInformation.LocalIdentifier,
- clone.TransactionInformation.DistributedIdentifier);
- }
- }
- this.scope = this.rpc.InstanceContext.Transaction.CreateTransactionScope(clone);
- this.transactionSetComplete = false;
- }
- }
- [MethodImpl(MethodImplOptions.NoInlining)]
- internal void ThreadLeave()
- {
- if (this.scope != null)
- {
- if (!this.transactionSetComplete)
- {
- this.scope.Complete();
- }
- try
- {
- this.scope.Dispose();
- this.scope = null;
- }
- catch (TransactionException e)
- {
- DiagnosticUtility.TraceHandledException(e, TraceEventType.Error);
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(TransactionBehavior.CreateFault(SR.GetString(SR.SFxTransactionAsyncAborted), FaultCodeConstants.Codes.TransactionAborted, true));
- }
- }
- }
- internal void CreateDependentClone()
- {
- if ((this.dependentClone == null) && (this.Clone != null))
- {
- this.dependentClone = this.Clone.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
- }
- }
- internal void CompleteDependentClone()
- {
- if (this.dependentClone != null)
- {
- this.dependentClone.Complete();
- }
- }
- }
- internal sealed class TransactionInstanceContextFacet
- {
- internal Transaction waiting; // waiting to become Single because Single is on his way out.
- internal Transaction Attached;
- IResumeMessageRpc paused; // the IResumeMessageRpc for this.waiting.
- object mutex;
- Transaction current; // the one true transaction when Concurrency=false.
- InstanceContext instanceContext;
- Dictionary<Transaction, RemoveReferenceRM> pending; // When Concurrency=true, all the still pending guys.
- bool shouldReleaseInstance = false;
- internal TransactionInstanceContextFacet(InstanceContext instanceContext)
- {
- this.instanceContext = instanceContext;
- this.mutex = instanceContext.ThisLock;
- }
- // ........................................................................................................
- // no need to lock the following property because it's used only if Concurrency = false
- internal bool ShouldReleaseInstance
- {
- get
- {
- return this.shouldReleaseInstance;
- }
- set
- {
- this.shouldReleaseInstance = value;
- }
- }
- // ........................................................................................................
- [MethodImpl(MethodImplOptions.NoInlining)]
- internal void CheckIfTxCompletedAndUpdateAttached(ref MessageRpc rpc, bool isConcurrent)
- {
- if (rpc.Transaction.Current == null)
- {
- return;
- }
- lock (this.mutex)
- {
- if (!isConcurrent)
- {
- if (this.shouldReleaseInstance)
- {
- this.shouldReleaseInstance = false;
- if (rpc.Error == null) //we don't want to mask the initial error
- {
- rpc.Error = TransactionBehavior.CreateFault(SR.GetString(SR.SFxTransactionAsyncAborted), FaultCodeConstants.Codes.TransactionAborted, true);
- DiagnosticUtility.TraceHandledException(rpc.Error, TraceEventType.Error);
- if (DiagnosticUtility.ShouldTraceInformation)
- {
- TraceUtility.TraceEvent(TraceEventType.Information,
- TraceCode.TxCompletionStatusCompletedForAsyncAbort,
- SR.GetString(SR.TraceCodeTxCompletionStatusCompletedForAsyncAbort,
- rpc.Transaction.Current.TransactionInformation.LocalIdentifier,
- rpc.Operation.Name)
- );
- }
- }
- }
- if (rpc.Transaction.IsCompleted || (rpc.Error != null))
- {
- if (DiagnosticUtility.ShouldTraceInformation)
- {
- if (rpc.Error != null)
- {
- TraceUtility.TraceEvent(TraceEventType.Information,
- TraceCode.TxCompletionStatusCompletedForError,
- SR.GetString(SR.TraceCodeTxCompletionStatusCompletedForError,
- rpc.Transaction.Current.TransactionInformation.LocalIdentifier,
- rpc.Operation.Name)
- );
- }
- else
- {
- TraceUtility.TraceEvent(TraceEventType.Information,
- TraceCode.TxCompletionStatusCompletedForAutocomplete,
- SR.GetString(SR.TraceCodeTxCompletionStatusCompletedForAutocomplete,
- rpc.Transaction.Current.TransactionInformation.LocalIdentifier,
- rpc.Operation.Name)
- );
- }
- }
- this.Attached = null;
- if (!(waiting == null))
- {
- // tx processing requires failfast when state is inconsistent
- DiagnosticUtility.FailFast("waiting should be null when resetting current");
- }
- this.current = null;
- }
- else
- {
- this.Attached = rpc.Transaction.Current;
- if (DiagnosticUtility.ShouldTraceInformation)
- {
- TraceUtility.TraceEvent(TraceEventType.Information,
- TraceCode.TxCompletionStatusRemainsAttached,
- SR.GetString(SR.TraceCodeTxCompletionStatusRemainsAttached,
- rpc.Transaction.Current.TransactionInformation.LocalIdentifier,
- rpc.Operation.Name)
- );
- }
- }
- }
- else if (!this.pending.ContainsKey(rpc.Transaction.Current))
- {
- //transaction has been asynchronously aborted
- if (rpc.Error == null) //we don't want to mask the initial error
- {
- rpc.Error = TransactionBehavior.CreateFault(SR.GetString(SR.SFxTransactionAsyncAborted), FaultCodeConstants.Codes.TransactionAborted, true);
- DiagnosticUtility.TraceHandledException(rpc.Error, TraceEventType.Error);
- if (DiagnosticUtility.ShouldTraceInformation)
- {
- TraceUtility.TraceEvent(TraceEventType.Information,
- TraceCode.TxCompletionStatusCompletedForAsyncAbort,
- SR.GetString(SR.TraceCodeTxCompletionStatusCompletedForAsyncAbort,
- rpc.Transaction.Current.TransactionInformation.LocalIdentifier,
- rpc.Operation.Name)
- );
- }
- }
- }
- }
- }
- // ........................................................................................................
- internal void CompletePendingTransaction(Transaction transaction, Exception error)
- {
- lock (this.mutex)
- {
- if (this.pending.ContainsKey(transaction))
- {
- Complete(transaction, error);
- }
- }
- }
- // ........................................................................................................
- internal static void Complete(Transaction transaction, Exception error)
- {
- try
- {
- if (error == null)
- {
- CommittableTransaction commit = (transaction as CommittableTransaction);
- if (commit != null)
- {
- commit.Commit();
- }
- else
- {
- DependentTransaction complete = (transaction as DependentTransaction);
- if (complete != null)
- {
- complete.Complete();
- }
- }
- }
- else
- {
- transaction.Rollback();
- }
- }
- catch (TransactionException e)
- {
- DiagnosticUtility.TraceHandledException(e, TraceEventType.Error);
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(TransactionBehavior.CreateFault(SR.GetString(SR.SFxTransactionAsyncAborted), FaultCodeConstants.Codes.TransactionAborted, true));
- }
- }
- // ........................................................................................................
- internal TransactionScope CreateTransactionScope(Transaction transaction)
- {
- lock (this.mutex)
- {
- if (this.pending.ContainsKey(transaction))
- {
- try
- {
- return new TransactionScope(transaction);
- }
- catch (TransactionException e)
- {
- DiagnosticUtility.TraceHandledException(e, TraceEventType.Error);
- //we'll rethrow below
- }
- }
- }
- //the transaction was asynchronously aborted
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(TransactionBehavior.CreateFault(SR.GetString(SR.SFxTransactionAsyncAborted), FaultCodeConstants.Codes.TransactionAborted, true));
- }
- // ........................................................................................................
- internal void SetCurrent(ref MessageRpc rpc)
- {
- Transaction requestTransaction = rpc.Transaction.Current;
- if (!(requestTransaction != null))
- {
- // tx processing requires failfast when state is inconsistent
- DiagnosticUtility.FailFast("we should never get here with a requestTransaction null");
- }
- lock (this.mutex)
- {
- if (this.current == null)
- {
- this.current = requestTransaction;
- }
- else if (this.current != requestTransaction)
- {
- this.waiting = requestTransaction;
- this.paused = rpc.Pause();
- }
- else
- {
- rpc.Transaction.Current = this.current; //rpc.Transaction.Current should get the dependent clone
- }
- }
- }
- // ........................................................................................................
- internal void AddReference(ref MessageRpc rpc, Transaction tx, bool updateCallCount)
- {
- lock (this.mutex)
- {
- if (this.pending == null)
- {
- this.pending = new Dictionary<Transaction, RemoveReferenceRM>();
- }
- if (tx != null)
- {
- if (this.pending == null)
- {
- this.pending = new Dictionary<Transaction, RemoveReferenceRM>();
- }
- RemoveReferenceRM rm;
- if (!this.pending.TryGetValue(tx, out rm))
- {
- RemoveReferenceRM rrm = new RemoveReferenceRM(this.instanceContext, tx, rpc.Operation.Name);
- rrm.CallCount = 1;
- this.pending.Add(tx, rrm);
- }
- else if (updateCallCount)
- {
- rm.CallCount += 1;
- }
- }
- }
- }
- internal void RemoveReference(Transaction tx)
- {
- lock (this.mutex)
- {
- if (tx.Equals(this.current))
- {
- if (this.waiting != null)
- {
- this.current = waiting;
- this.waiting = null;
- if (instanceContext.Behavior.ReleaseServiceInstanceOnTransactionComplete)
- {
- instanceContext.ReleaseServiceInstance();
- if (DiagnosticUtility.ShouldTraceInformation)
- {
- TraceUtility.TraceEvent(TraceEventType.Information,
- TraceCode.TxReleaseServiceInstanceOnCompletion,
- SR.GetString(SR.TraceCodeTxReleaseServiceInstanceOnCompletion,
- tx.TransactionInformation.LocalIdentifier)
- );
- }
- }
- bool alreadyResumedNoLock;
- this.paused.Resume(out alreadyResumedNoLock);
- if (alreadyResumedNoLock)
- {
- Fx.Assert("TransactionBehavior resumed more than once for same call.");
- }
- }
- else
- {
- this.shouldReleaseInstance = true;
- this.current = null;
- }
- }
- if (this.pending != null)
- {
- if (this.pending.ContainsKey(tx))
- {
- this.pending.Remove(tx);
- }
- }
- }
- }
- // ........................................................................................................
- abstract class VolatileBase : ISinglePhaseNotification
- {
- protected InstanceContext InstanceContext;
- protected Transaction Transaction;
- protected VolatileBase(InstanceContext instanceContext, Transaction transaction)
- {
- this.InstanceContext = instanceContext;
- this.Transaction = transaction;
- this.Transaction.EnlistVolatile(this, EnlistmentOptions.None);
- }
- protected abstract void Completed();
- public virtual void Commit(Enlistment enlistment)
- {
- this.Completed();
- }
- public virtual void InDoubt(Enlistment enlistment)
- {
- this.Completed();
- }
- public virtual void Rollback(Enlistment enlistment)
- {
- this.Completed();
- }
- public virtual void SinglePhaseCommit(SinglePhaseEnlistment enlistment)
- {
- enlistment.Committed();
- this.Completed();
- }
- public void Prepare(PreparingEnlistment preparingEnlistment)
- {
- preparingEnlistment.Prepared();
- }
- }
- sealed class RemoveReferenceRM : VolatileBase
- {
- string operation;
- long callCount = 0;
- EndpointDispatcher endpointDispatcher;
- internal RemoveReferenceRM(InstanceContext instanceContext, Transaction tx, string operation)
- : base(instanceContext, tx)
- {
- this.operation = operation;
- if (PerformanceCounters.PerformanceCountersEnabled)
- {
- this.endpointDispatcher = PerformanceCounters.GetEndpointDispatcher();
- }
- AspNetEnvironment.Current.IncrementBusyCount();
- if (AspNetEnvironment.Current.TraceIncrementBusyCountIsEnabled())
- {
- AspNetEnvironment.Current.TraceIncrementBusyCount(this.GetType().FullName);
- }
- }
- internal long CallCount
- {
- get { return this.callCount; }
- set { this.callCount = value; }
- }
- protected override void Completed()
- {
- this.InstanceContext.Transaction.RemoveReference(this.Transaction);
- AspNetEnvironment.Current.DecrementBusyCount();
- if (AspNetEnvironment.Current.TraceDecrementBusyCountIsEnabled())
- {
- AspNetEnvironment.Current.TraceDecrementBusyCount(this.GetType().FullName);
- }
- }
- public override void SinglePhaseCommit(SinglePhaseEnlistment enlistment)
- {
- if (PerformanceCounters.PerformanceCountersEnabled)
- {
- PerformanceCounters.TxCommitted(this.endpointDispatcher, CallCount);
- }
- base.SinglePhaseCommit(enlistment);
- }
- public override void Commit(Enlistment enlistment)
- {
- if (PerformanceCounters.PerformanceCountersEnabled)
- {
- PerformanceCounters.TxCommitted(this.endpointDispatcher, CallCount);
- }
- base.Commit(enlistment);
- }
- public override void Rollback(Enlistment enlistment)
- {
- if (PerformanceCounters.PerformanceCountersEnabled)
- {
- PerformanceCounters.TxAborted(this.endpointDispatcher, CallCount);
- }
- if (DiagnosticUtility.ShouldTraceInformation)
- {
- TraceUtility.TraceEvent(TraceEventType.Information,
- TraceCode.TxAsyncAbort,
- SR.GetString(SR.TraceCodeTxAsyncAbort,
- this.Transaction.TransactionInformation.LocalIdentifier)
- );
- }
- base.Rollback(enlistment);
- }
- public override void InDoubt(Enlistment enlistment)
- {
- if (PerformanceCounters.PerformanceCountersEnabled)
- {
- PerformanceCounters.TxInDoubt(this.endpointDispatcher, CallCount);
- }
- base.InDoubt(enlistment);
- }
- }
- }
- internal enum ExclusiveInstanceContextTransactionResult
- {
- Acquired, Wait, Fault
- };
- }
|