Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / tx / System / Transactions / TransactionScope.cs / 1305376 / TransactionScope.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- using System; using System.Diagnostics; using SysES = System.EnterpriseServices; using System.Runtime.Remoting.Messaging; using System.Runtime.InteropServices; using System.Threading; using System.Transactions.Diagnostics; namespace System.Transactions { public enum TransactionScopeOption { Required, RequiresNew, Suppress, } public enum EnterpriseServicesInteropOption { None = 0, Automatic = 1, Full = 2 } public sealed class TransactionScope : IDisposable { public TransactionScope() : this( TransactionScopeOption.Required ) { } public TransactionScope( TransactionScopeOption scopeOption ) { if( !TransactionManager._platformValidated ) TransactionManager.ValidatePlatform(); if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( TransactionScopeOption )" ); } if ( NeedToCreateTransaction( scopeOption ) ) { committableTransaction = new CommittableTransaction(); expectedCurrent = committableTransaction.Clone(); } if ( DiagnosticTrace.Information ) { if ( null == expectedCurrent ) { TransactionScopeCreatedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), TransactionTraceIdentifier.Empty, TransactionScopeResult.NoTransaction ); } else { TransactionScopeResult scopeResult; if ( null == committableTransaction ) { scopeResult = TransactionScopeResult.UsingExistingCurrent; } else { scopeResult = TransactionScopeResult.CreatedTransaction; } TransactionScopeCreatedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), expectedCurrent.TransactionTraceId, scopeResult ); } } PushScope(); if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( TransactionScopeOption )" ); } } public TransactionScope( TransactionScopeOption scopeOption, TimeSpan scopeTimeout ) { if( !TransactionManager._platformValidated ) TransactionManager.ValidatePlatform(); if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( TransactionScopeOption, TimeSpan )" ); } ValidateScopeTimeout( "scopeTimeout", scopeTimeout ); TimeSpan txTimeout = TransactionManager.ValidateTimeout( scopeTimeout ); if( NeedToCreateTransaction( scopeOption )) { this.committableTransaction = new CommittableTransaction( txTimeout ); this.expectedCurrent = committableTransaction.Clone(); } if( (null != this.expectedCurrent) && (null == this.committableTransaction) && (TimeSpan.Zero != scopeTimeout) ) { // scopeTimer = new Timer( TransactionScope.TimerCallback, this, scopeTimeout, TimeSpan.Zero ); } if ( DiagnosticTrace.Information ) { if ( null == expectedCurrent ) { TransactionScopeCreatedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), TransactionTraceIdentifier.Empty, TransactionScopeResult.NoTransaction ); } else { TransactionScopeResult scopeResult; if ( null == committableTransaction ) { scopeResult = TransactionScopeResult.UsingExistingCurrent; } else { scopeResult = TransactionScopeResult.CreatedTransaction; } TransactionScopeCreatedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), expectedCurrent.TransactionTraceId, scopeResult ); } } PushScope(); if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( TransactionScopeOption, TimeSpan )" ); } } public TransactionScope( TransactionScopeOption scopeOption, TransactionOptions transactionOptions ) { if( !TransactionManager._platformValidated ) TransactionManager.ValidatePlatform(); if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( TransactionScopeOption, TransactionOptions )" ); } ValidateScopeTimeout( "transactionOptions.Timeout", transactionOptions.Timeout ); TimeSpan scopeTimeout = transactionOptions.Timeout; transactionOptions.Timeout = TransactionManager.ValidateTimeout( transactionOptions.Timeout ); TransactionManager.ValidateIsolationLevel( transactionOptions.IsolationLevel ); if( NeedToCreateTransaction( scopeOption ) ) { this.committableTransaction = new CommittableTransaction( transactionOptions ); this.expectedCurrent = committableTransaction.Clone(); } else { if ( null != this.expectedCurrent ) { // If the requested IsolationLevel is stronger than that of the specified transaction, throw. if ( (IsolationLevel.Unspecified != transactionOptions.IsolationLevel) && ( expectedCurrent.IsolationLevel != transactionOptions.IsolationLevel ) ) { throw new ArgumentException( SR.GetString( SR.TransactionScopeIsolationLevelDifferentFromTransaction ), "transactionOptions.IsolationLevel" ); } } } if( (null != this.expectedCurrent) && (null == this.committableTransaction) && (TimeSpan.Zero != scopeTimeout) ) { // scopeTimer = new Timer( TransactionScope.TimerCallback, this, scopeTimeout, TimeSpan.Zero ); } if ( DiagnosticTrace.Information ) { if ( null == expectedCurrent ) { TransactionScopeCreatedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), TransactionTraceIdentifier.Empty, TransactionScopeResult.NoTransaction ); } else { TransactionScopeResult scopeResult; if ( null == committableTransaction ) { scopeResult = TransactionScopeResult.UsingExistingCurrent; } else { scopeResult = TransactionScopeResult.CreatedTransaction; } TransactionScopeCreatedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), expectedCurrent.TransactionTraceId, scopeResult ); } } PushScope(); if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( TransactionScopeOption, TransactionOptions )" ); } } [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] public TransactionScope( TransactionScopeOption scopeOption, TransactionOptions transactionOptions, EnterpriseServicesInteropOption interopOption ) { if( !TransactionManager._platformValidated ) TransactionManager.ValidatePlatform(); if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( TransactionScopeOption, TransactionOptions, EnterpriseServicesInteropOption )" ); } ValidateScopeTimeout( "transactionOptions.Timeout", transactionOptions.Timeout ); TimeSpan scopeTimeout = transactionOptions.Timeout; transactionOptions.Timeout = TransactionManager.ValidateTimeout( transactionOptions.Timeout ); TransactionManager.ValidateIsolationLevel( transactionOptions.IsolationLevel ); ValidateInteropOption( interopOption ); this.interopModeSpecified = true; this.interopOption = interopOption; if( NeedToCreateTransaction( scopeOption ) ) { committableTransaction = new CommittableTransaction( transactionOptions ); expectedCurrent = committableTransaction.Clone(); } else { if ( null != expectedCurrent ) { // If the requested IsolationLevel is stronger than that of the specified transaction, throw. if ( (IsolationLevel.Unspecified != transactionOptions.IsolationLevel) && ( expectedCurrent.IsolationLevel != transactionOptions.IsolationLevel ) ) { throw new ArgumentException( SR.GetString( SR.TransactionScopeIsolationLevelDifferentFromTransaction ), "transactionOptions.IsolationLevel" ); } } } if( (null != this.expectedCurrent) && (null == this.committableTransaction) && (TimeSpan.Zero != scopeTimeout) ) { // scopeTimer = new Timer( TransactionScope.TimerCallback, this, scopeTimeout, TimeSpan.Zero ); } if ( DiagnosticTrace.Information ) { if ( null == expectedCurrent ) { TransactionScopeCreatedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), TransactionTraceIdentifier.Empty, TransactionScopeResult.NoTransaction ); } else { TransactionScopeResult scopeResult; if ( null == committableTransaction ) { scopeResult = TransactionScopeResult.UsingExistingCurrent; } else { scopeResult = TransactionScopeResult.CreatedTransaction; } TransactionScopeCreatedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), expectedCurrent.TransactionTraceId, scopeResult ); } } PushScope(); if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( TransactionScopeOption, TransactionOptions, EnterpriseServicesInteropOption )" ); } } public TransactionScope( Transaction transactionToUse ) { if( !TransactionManager._platformValidated ) TransactionManager.ValidatePlatform(); if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( Transaction )" ); } Initialize( transactionToUse, TimeSpan.Zero, false ); if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( Transaction )" ); } } public TransactionScope( Transaction transactionToUse, TimeSpan scopeTimeout ) { if( !TransactionManager._platformValidated ) TransactionManager.ValidatePlatform(); if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( Transaction, TimeSpan )" ); } Initialize( transactionToUse, scopeTimeout, false ); if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( Transaction, TimeSpan )" ); } } [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] public TransactionScope( Transaction transactionToUse, TimeSpan scopeTimeout, EnterpriseServicesInteropOption interopOption ) { if( !TransactionManager._platformValidated ) TransactionManager.ValidatePlatform(); if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( Transaction, TimeSpan, EnterpriseServicesInteropOption )" ); } ValidateInteropOption( interopOption ); this.interopOption = interopOption; Initialize( transactionToUse, scopeTimeout, true ); if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( Transaction, TimeSpan, EnterpriseServicesInteropOption )" ); } } private bool NeedToCreateTransaction( TransactionScopeOption scopeOption ) { bool retVal = false; CommonInitialize(); // If the options specify NoTransactionNeeded, that trumps everything else. switch( scopeOption ) { case TransactionScopeOption.Suppress: expectedCurrent = null; retVal = false; break; case TransactionScopeOption.Required: expectedCurrent = this.savedCurrent; // If current is null, we need to create one. if ( null == expectedCurrent ) { retVal = true; } break; case TransactionScopeOption.RequiresNew: retVal = true; break; default: throw new ArgumentOutOfRangeException( "scopeOption" ); } return retVal; } private void Initialize( Transaction transactionToUse, TimeSpan scopeTimeout, bool interopModeSpecified ) { if ( null == transactionToUse ) { throw new ArgumentNullException( "transactionToUse" ); } ValidateScopeTimeout( "scopeTimeout", scopeTimeout ); CommonInitialize(); if ( TimeSpan.Zero != scopeTimeout ) { scopeTimer = new Timer( TransactionScope.TimerCallback, this, scopeTimeout, TimeSpan.Zero ); } this.expectedCurrent = transactionToUse; this.interopModeSpecified = interopModeSpecified; if ( DiagnosticTrace.Information ) { TransactionScopeCreatedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), expectedCurrent.TransactionTraceId, TransactionScopeResult.TransactionPassed ); } PushScope(); } // We don't have a finalizer (~TransactionScope) because all it would be able to do is try to // operate on other managed objects (the transaction), which is not safe to do because they may // already have been finalized. // FXCop wants us to dispose savedCurrent, which is a Transaction, and thus disposable. But we don't // want to do that here. We want to restore Current to that value. We do dispose the expected current. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed")] public void Dispose() { bool successful = false; if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.Dispose" ); } if ( this.disposed ) { if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.Dispose" ); } return; } // Dispose for a scope can only be called on the thread where the scope was created. if( this.scopeThread != Thread.CurrentThread ) { if ( DiagnosticTrace.Error ) { InvalidOperationExceptionTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), SR.GetString( SR.InvalidScopeThread ) ); } throw new InvalidOperationException( SR.GetString( SR.InvalidScopeThread )); } Exception exToThrow = null; try { // Single threaded from this point this.disposed = true; // First, lets pop the "stack" of TransactionScopes and dispose each one that is above us in // the stack, making sure they are NOT consistent before disposing them. // Optimize the first lookup by getting both the actual current scope and actual current // transaction at the same time. TransactionScope actualCurrentScope = this.threadContextData.CurrentScope; Transaction contextTransaction = null; Transaction current = Transaction.FastGetTransaction( actualCurrentScope, this.threadContextData, out contextTransaction ); if( !Equals( actualCurrentScope )) { // Ok this is bad. But just how bad is it. The worst case scenario is that someone is // poping scopes out of order and has placed a new transaction in the top level scope. // Check for that now. if( actualCurrentScope == null ) { // Something must have gone wrong trying to clean up a bad scope // stack previously. // Make a best effort to abort the active transaction. Transaction rollbackTransaction = this.committableTransaction; if( rollbackTransaction == null ) { rollbackTransaction = this.dependentTransaction; } Debug.Assert( rollbackTransaction != null ); rollbackTransaction.Rollback(); successful = true; throw TransactionException.CreateInvalidOperationException( SR.GetString( SR.TraceSourceBase ), SR.GetString( SR.TransactionScopeInvalidNesting ), null ); } // Verify that expectedCurrent is the same as the "current" current if we the interopOption value is None. else if( EnterpriseServicesInteropOption.None == actualCurrentScope.interopOption ) { if ( ( ( null != actualCurrentScope.expectedCurrent ) && ( ! actualCurrentScope.expectedCurrent.Equals( current ) ) ) || ( ( null != current ) && ( null == actualCurrentScope.expectedCurrent ) ) ) { if ( DiagnosticTrace.Warning ) { TransactionTraceIdentifier myId; TransactionTraceIdentifier currentId; if ( null == current ) { currentId = TransactionTraceIdentifier.Empty; } else { currentId = current.TransactionTraceId; } if ( null == this.expectedCurrent ) { myId = TransactionTraceIdentifier.Empty; } else { myId = this.expectedCurrent.TransactionTraceId; } TransactionScopeCurrentChangedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), myId, currentId ); } exToThrow = TransactionException.CreateInvalidOperationException( SR.GetString( SR.TraceSourceBase ), SR.GetString( SR.TransactionScopeIncorrectCurrent ), null ); // If there is a current transaction, abort it. if ( null != current ) { try { current.Rollback(); } catch ( TransactionException ) { // we are already going to throw and exception, so just ignore this one. } catch ( ObjectDisposedException ) { // Dito } } } } // Now fix up the scopes while ( !Equals( actualCurrentScope )) { if ( null == exToThrow ) { exToThrow = TransactionException.CreateInvalidOperationException( SR.GetString( SR.TraceSourceBase ), SR.GetString( SR.TransactionScopeInvalidNesting ), null ); } if ( DiagnosticTrace.Warning ) { if ( null == actualCurrentScope.expectedCurrent ) { TransactionScopeNestedIncorrectlyTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), TransactionTraceIdentifier.Empty ); } else { TransactionScopeNestedIncorrectlyTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), actualCurrentScope.expectedCurrent.TransactionTraceId ); } } actualCurrentScope.complete = false; try { actualCurrentScope.InternalDispose(); } catch ( TransactionException ) { // we are already going to throw an exception, so just ignore this one. } actualCurrentScope = this.threadContextData.CurrentScope; // We want to fail this scope, too, because work may have been done in one of these other // nested scopes that really should have been done in my scope. this.complete = false; } } else { // Verify that expectedCurrent is the same as the "current" current if we the interopOption value is None. // If we got here, actualCurrentScope is the same as "this". if( EnterpriseServicesInteropOption.None == this.interopOption ) { if ((( null != this.expectedCurrent ) && ( ! this.expectedCurrent.Equals( current ))) || (( null != current ) && ( null == this.expectedCurrent )) ) { if ( DiagnosticTrace.Warning ) { TransactionTraceIdentifier myId; TransactionTraceIdentifier currentId; if ( null == current ) { currentId = TransactionTraceIdentifier.Empty; } else { currentId = current.TransactionTraceId; } if ( null == this.expectedCurrent ) { myId = TransactionTraceIdentifier.Empty; } else { myId = this.expectedCurrent.TransactionTraceId; } TransactionScopeCurrentChangedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), myId, currentId ); } if ( null == exToThrow ) { exToThrow = TransactionException.CreateInvalidOperationException( SR.GetString( SR.TraceSourceBase ), SR.GetString( SR.TransactionScopeIncorrectCurrent ), null ); } // If there is a current transaction, abort it. if ( null != current ) { try { current.Rollback(); } catch ( TransactionException ) { // we are already going to throw and exception, so just ignore this one. } catch ( ObjectDisposedException ) { // Dito } } // Set consistent to false so that the subsequent call to // InternalDispose below will rollback this.expectedCurrent. this.complete = false; } } } successful = true; } finally { if ( !successful ) { PopScope(); } } // No try..catch here. Just let any exception thrown by InternalDispose go out. InternalDispose(); if ( null != exToThrow ) { throw exToThrow; } if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.Dispose" ); } } private void InternalDispose() { // Set this if it is called internally. this.disposed = true; try { PopScope(); if ( DiagnosticTrace.Information ) { if ( null == this.expectedCurrent ) { TransactionScopeDisposedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), TransactionTraceIdentifier.Empty ); } else { TransactionScopeDisposedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), this.expectedCurrent.TransactionTraceId ); } } // If Transaction.Current is not null, we have work to do. Otherwise, we don't, except to replace // the previous value. if( null != this.expectedCurrent ) { if( !this.complete ) { if ( DiagnosticTrace.Warning ) { TransactionScopeIncompleteTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), this.expectedCurrent.TransactionTraceId ); } // // Note: Rollback is not called on expected current because someone could conceiveably // dispose expectedCurrent out from under the transaction scope. // Transaction rollbackTransaction = this.committableTransaction; if( rollbackTransaction == null ) { rollbackTransaction = this.dependentTransaction; } Debug.Assert( rollbackTransaction != null ); rollbackTransaction.Rollback(); } else { // If we are supposed to commit on dispose, cast to CommittableTransaction and commit it. if ( null != this.committableTransaction ) { this.committableTransaction.Commit(); } else { Debug.Assert( null != this.dependentTransaction, "null != this.dependentTransaction" ); this.dependentTransaction.Complete(); } } } } finally { if ( null != scopeTimer ) { scopeTimer.Dispose(); } if ( null != this.committableTransaction ) { this.committableTransaction.Dispose(); // If we created the committable transaction then we placed a clone in expectedCurrent // and it needs to be disposed as well. this.expectedCurrent.Dispose(); } if ( null != this.dependentTransaction ) { this.dependentTransaction.Dispose(); } } } public void Complete() { if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.Complete" ); } if( this.disposed ) { throw new ObjectDisposedException( "TransactionScope" ); } if( this.complete ) { throw TransactionException.CreateInvalidOperationException( SR.GetString( SR.TraceSourceBase ), SR.GetString(SR.DisposeScope), null); } this.complete = true; if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.Complete" ); } } private static void TimerCallback( object state ) { TransactionScope scope = state as TransactionScope; if ( null == scope ) { if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), SR.GetString( SR.TransactionScopeTimerObjectInvalid ) ); } throw TransactionException.Create( SR.GetString( SR.TraceSourceBase ), SR.GetString( SR.InternalError) + SR.GetString( SR.TransactionScopeTimerObjectInvalid ), null ); } scope.Timeout(); } private void Timeout() { if( ( !this.complete ) && ( null != this.expectedCurrent ) ) { if ( DiagnosticTrace.Warning ) { TransactionScopeTimeoutTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), this.expectedCurrent.TransactionTraceId ); } try { this.expectedCurrent.Rollback(); } catch( ObjectDisposedException ex ) { // Tolerate the fact that the transaction has already been disposed. if ( DiagnosticTrace.Verbose ) { ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), ex ); } } catch ( TransactionException txEx ) { // Tolerate transaction exceptions - VSWhidbey 466868. if ( DiagnosticTrace.Verbose ) { ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), txEx ); } } } } private void CommonInitialize() { this.complete = false; this.dependentTransaction = null; this.disposed = false; this.committableTransaction = null; this.expectedCurrent = null; this.scopeTimer = null; this.scopeThread = Thread.CurrentThread; Transaction.GetCurrentTransactionAndScope( out this.savedCurrent, out this.savedCurrentScope, out this.threadContextData, out this.contextTransaction ); } // PushScope // // Push a transaction scope onto the stack. private void PushScope() { // Fixup the interop mode before we set current. if( !this.interopModeSpecified ) { // Transaction.InteropMode will take the interop mode on // for the scope in currentScope into account. this.interopOption = Transaction.InteropMode(this.savedCurrentScope); } // This call needs to be done first SetCurrent( expectedCurrent ); this.threadContextData.CurrentScope = this; } // PopScope // // Pop the current transaction scope off the top of the stack private void PopScope() { this.threadContextData.CurrentScope = this.savedCurrentScope; RestoreCurrent(); } // SetCurrent // // Place the given value in current by whatever means necessary for interop mode. private void SetCurrent( Transaction newCurrent ) { // Keep a dependent clone of current if we don't have one and we are not committable if( this.dependentTransaction == null && this.committableTransaction == null ) { if( newCurrent != null ) { this.dependentTransaction = newCurrent.DependentClone( DependentCloneOption.RollbackIfNotComplete ); } } switch( this.interopOption ) { case EnterpriseServicesInteropOption.None: this.threadContextData.CurrentTransaction = newCurrent; break; case EnterpriseServicesInteropOption.Automatic: Transaction.VerifyEnterpriseServicesOk(); if( Transaction.UseServiceDomainForCurrent() ) { PushServiceDomain( newCurrent ); } else { this.threadContextData.CurrentTransaction = newCurrent; } break; case EnterpriseServicesInteropOption.Full: Transaction.VerifyEnterpriseServicesOk(); PushServiceDomain( newCurrent ); break; } } // PushServiceDomain // // Create a new service domain with the given transaction. [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] // PushServiceDomain will only be called if a transaction scope is created with an // EnterpriseServicesInteropOption. TransactionScope constructors that take a ESIO are protected by // a FullTrust link demand. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2116:AptcaMethodsShouldOnlyCallAptcaMethods")] private void PushServiceDomain( Transaction newCurrent ) { // If we are not changing the transaction for the current ServiceDomain then // don't call CoEnterServiceDomain if ((newCurrent != null && newCurrent.Equals( SysES.ContextUtil.SystemTransaction )) || (newCurrent == null && SysES.ContextUtil.SystemTransaction == null )) { return; } SysES.ServiceConfig serviceConfig = new SysES.ServiceConfig(); try { // If a transaction is specified place it in BYOT. Otherwise the // default transaction option for ServiceConfig is disabled. So // if this is a not supported scope it will be cleared. if( newCurrent != null ) { // To work around an SWC bug in Com+ we need to create 2 // service domains. // Com+ will by default try to inherit synchronization from // an existing context. Turn that off. serviceConfig.Synchronization = SysES.SynchronizationOption.RequiresNew; SysES.ServiceDomain.Enter( serviceConfig ); this.createdDoubleServiceDomain = true; serviceConfig.Synchronization = SysES.SynchronizationOption.Required; serviceConfig.BringYourOwnSystemTransaction = newCurrent; } SysES.ServiceDomain.Enter( serviceConfig ); this.createdServiceDomain = true; } catch( COMException e ) { if ( System.Transactions.Oletx.NativeMethods.XACT_E_NOTRANSACTION == e.ErrorCode ) { throw TransactionException.Create( SR.GetString( SR.TraceSourceBase ), SR.GetString( SR.TransactionAlreadyOver ) , e ); } throw TransactionException.Create( SR.GetString( SR.TraceSourceBase ), e.Message, e ); } finally { if( !this.createdServiceDomain ) { // If we weren't successful in creating both service domains then // make sure to exit one of them. if( this.createdDoubleServiceDomain ) { SysES.ServiceDomain.Leave(); } } } } [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] // PushServiceDomain will only be called if a transaction scope is created with an // EnterpriseServicesInteropOption. TransactionScope constructors that take a ESIO are protected by // a FullTrust link demand. Therefore JitSafeLeaveServiceDomain is not exposed directly to a partial // trust caller. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2116:AptcaMethodsShouldOnlyCallAptcaMethods")] private void JitSafeLeaveServiceDomain() { if( this.createdDoubleServiceDomain ) { // This is an ugly work around to a bug in Com+ that prevents the use of BYOT and SWC with // anything other than required synchronization. SysES.ServiceDomain.Leave(); } SysES.ServiceDomain.Leave(); } // RestoreCurrent // // Restore current to it's previous value depending on how it was changed for this scope. private void RestoreCurrent() { if( this.createdServiceDomain ) { JitSafeLeaveServiceDomain(); } // Only restore the value that was actually in the context. this.threadContextData.CurrentTransaction = this.contextTransaction; } // ValidateInteropOption // // Validate a given interop Option private void ValidateInteropOption( EnterpriseServicesInteropOption interopOption ) { if( interopOption < EnterpriseServicesInteropOption.None || interopOption > EnterpriseServicesInteropOption.Full ) { throw new ArgumentOutOfRangeException( "interopOption" ); } } // ValidateScopeTimeout // // Scope timeouts are not governed by MaxTimeout and therefore need a special validate function private void ValidateScopeTimeout( string paramName, TimeSpan scopeTimeout ) { if( scopeTimeout < TimeSpan.Zero ) { throw new ArgumentOutOfRangeException( paramName ); } } // Denotes the action to take when the scope is disposed. bool complete; internal bool ScopeComplete { get { return this.complete; } } // Storage location for the previous current transaction. Transaction savedCurrent; // To ensure that we don't restore a value for current that was // returned to us by an external entity keep the value that was actually // in TLS when the scope was created. Transaction contextTransaction; // Storage for the value to restore to current TransactionScope savedCurrentScope; // Store a reference to the context data object for this scope. ContextData threadContextData; // Store a reference to the value that this scope expects for current Transaction expectedCurrent; // Store a reference to the committable form of this transaction if // the scope made one. CommittableTransaction committableTransaction; // Store a reference to the scopes transaction guard. DependentTransaction dependentTransaction; // Note when the scope is disposed. bool disposed; // Timer scopeTimer; // Store a reference to the thread on which the scope was created so that we can // check to make sure that the dispose pattern for scope is being used correctly. Thread scopeThread; // Store a member to let us know if a new service domain has been created for this // scope. bool createdServiceDomain; bool createdDoubleServiceDomain; // Store the interop mode for this transaction scope. bool interopModeSpecified = false; EnterpriseServicesInteropOption interopOption; internal EnterpriseServicesInteropOption InteropMode { get { return this.interopOption; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- using System; using System.Diagnostics; using SysES = System.EnterpriseServices; using System.Runtime.Remoting.Messaging; using System.Runtime.InteropServices; using System.Threading; using System.Transactions.Diagnostics; namespace System.Transactions { public enum TransactionScopeOption { Required, RequiresNew, Suppress, } public enum EnterpriseServicesInteropOption { None = 0, Automatic = 1, Full = 2 } public sealed class TransactionScope : IDisposable { public TransactionScope() : this( TransactionScopeOption.Required ) { } public TransactionScope( TransactionScopeOption scopeOption ) { if( !TransactionManager._platformValidated ) TransactionManager.ValidatePlatform(); if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( TransactionScopeOption )" ); } if ( NeedToCreateTransaction( scopeOption ) ) { committableTransaction = new CommittableTransaction(); expectedCurrent = committableTransaction.Clone(); } if ( DiagnosticTrace.Information ) { if ( null == expectedCurrent ) { TransactionScopeCreatedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), TransactionTraceIdentifier.Empty, TransactionScopeResult.NoTransaction ); } else { TransactionScopeResult scopeResult; if ( null == committableTransaction ) { scopeResult = TransactionScopeResult.UsingExistingCurrent; } else { scopeResult = TransactionScopeResult.CreatedTransaction; } TransactionScopeCreatedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), expectedCurrent.TransactionTraceId, scopeResult ); } } PushScope(); if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( TransactionScopeOption )" ); } } public TransactionScope( TransactionScopeOption scopeOption, TimeSpan scopeTimeout ) { if( !TransactionManager._platformValidated ) TransactionManager.ValidatePlatform(); if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( TransactionScopeOption, TimeSpan )" ); } ValidateScopeTimeout( "scopeTimeout", scopeTimeout ); TimeSpan txTimeout = TransactionManager.ValidateTimeout( scopeTimeout ); if( NeedToCreateTransaction( scopeOption )) { this.committableTransaction = new CommittableTransaction( txTimeout ); this.expectedCurrent = committableTransaction.Clone(); } if( (null != this.expectedCurrent) && (null == this.committableTransaction) && (TimeSpan.Zero != scopeTimeout) ) { // scopeTimer = new Timer( TransactionScope.TimerCallback, this, scopeTimeout, TimeSpan.Zero ); } if ( DiagnosticTrace.Information ) { if ( null == expectedCurrent ) { TransactionScopeCreatedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), TransactionTraceIdentifier.Empty, TransactionScopeResult.NoTransaction ); } else { TransactionScopeResult scopeResult; if ( null == committableTransaction ) { scopeResult = TransactionScopeResult.UsingExistingCurrent; } else { scopeResult = TransactionScopeResult.CreatedTransaction; } TransactionScopeCreatedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), expectedCurrent.TransactionTraceId, scopeResult ); } } PushScope(); if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( TransactionScopeOption, TimeSpan )" ); } } public TransactionScope( TransactionScopeOption scopeOption, TransactionOptions transactionOptions ) { if( !TransactionManager._platformValidated ) TransactionManager.ValidatePlatform(); if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( TransactionScopeOption, TransactionOptions )" ); } ValidateScopeTimeout( "transactionOptions.Timeout", transactionOptions.Timeout ); TimeSpan scopeTimeout = transactionOptions.Timeout; transactionOptions.Timeout = TransactionManager.ValidateTimeout( transactionOptions.Timeout ); TransactionManager.ValidateIsolationLevel( transactionOptions.IsolationLevel ); if( NeedToCreateTransaction( scopeOption ) ) { this.committableTransaction = new CommittableTransaction( transactionOptions ); this.expectedCurrent = committableTransaction.Clone(); } else { if ( null != this.expectedCurrent ) { // If the requested IsolationLevel is stronger than that of the specified transaction, throw. if ( (IsolationLevel.Unspecified != transactionOptions.IsolationLevel) && ( expectedCurrent.IsolationLevel != transactionOptions.IsolationLevel ) ) { throw new ArgumentException( SR.GetString( SR.TransactionScopeIsolationLevelDifferentFromTransaction ), "transactionOptions.IsolationLevel" ); } } } if( (null != this.expectedCurrent) && (null == this.committableTransaction) && (TimeSpan.Zero != scopeTimeout) ) { // scopeTimer = new Timer( TransactionScope.TimerCallback, this, scopeTimeout, TimeSpan.Zero ); } if ( DiagnosticTrace.Information ) { if ( null == expectedCurrent ) { TransactionScopeCreatedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), TransactionTraceIdentifier.Empty, TransactionScopeResult.NoTransaction ); } else { TransactionScopeResult scopeResult; if ( null == committableTransaction ) { scopeResult = TransactionScopeResult.UsingExistingCurrent; } else { scopeResult = TransactionScopeResult.CreatedTransaction; } TransactionScopeCreatedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), expectedCurrent.TransactionTraceId, scopeResult ); } } PushScope(); if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( TransactionScopeOption, TransactionOptions )" ); } } [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] public TransactionScope( TransactionScopeOption scopeOption, TransactionOptions transactionOptions, EnterpriseServicesInteropOption interopOption ) { if( !TransactionManager._platformValidated ) TransactionManager.ValidatePlatform(); if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( TransactionScopeOption, TransactionOptions, EnterpriseServicesInteropOption )" ); } ValidateScopeTimeout( "transactionOptions.Timeout", transactionOptions.Timeout ); TimeSpan scopeTimeout = transactionOptions.Timeout; transactionOptions.Timeout = TransactionManager.ValidateTimeout( transactionOptions.Timeout ); TransactionManager.ValidateIsolationLevel( transactionOptions.IsolationLevel ); ValidateInteropOption( interopOption ); this.interopModeSpecified = true; this.interopOption = interopOption; if( NeedToCreateTransaction( scopeOption ) ) { committableTransaction = new CommittableTransaction( transactionOptions ); expectedCurrent = committableTransaction.Clone(); } else { if ( null != expectedCurrent ) { // If the requested IsolationLevel is stronger than that of the specified transaction, throw. if ( (IsolationLevel.Unspecified != transactionOptions.IsolationLevel) && ( expectedCurrent.IsolationLevel != transactionOptions.IsolationLevel ) ) { throw new ArgumentException( SR.GetString( SR.TransactionScopeIsolationLevelDifferentFromTransaction ), "transactionOptions.IsolationLevel" ); } } } if( (null != this.expectedCurrent) && (null == this.committableTransaction) && (TimeSpan.Zero != scopeTimeout) ) { // scopeTimer = new Timer( TransactionScope.TimerCallback, this, scopeTimeout, TimeSpan.Zero ); } if ( DiagnosticTrace.Information ) { if ( null == expectedCurrent ) { TransactionScopeCreatedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), TransactionTraceIdentifier.Empty, TransactionScopeResult.NoTransaction ); } else { TransactionScopeResult scopeResult; if ( null == committableTransaction ) { scopeResult = TransactionScopeResult.UsingExistingCurrent; } else { scopeResult = TransactionScopeResult.CreatedTransaction; } TransactionScopeCreatedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), expectedCurrent.TransactionTraceId, scopeResult ); } } PushScope(); if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( TransactionScopeOption, TransactionOptions, EnterpriseServicesInteropOption )" ); } } public TransactionScope( Transaction transactionToUse ) { if( !TransactionManager._platformValidated ) TransactionManager.ValidatePlatform(); if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( Transaction )" ); } Initialize( transactionToUse, TimeSpan.Zero, false ); if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( Transaction )" ); } } public TransactionScope( Transaction transactionToUse, TimeSpan scopeTimeout ) { if( !TransactionManager._platformValidated ) TransactionManager.ValidatePlatform(); if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( Transaction, TimeSpan )" ); } Initialize( transactionToUse, scopeTimeout, false ); if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( Transaction, TimeSpan )" ); } } [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] public TransactionScope( Transaction transactionToUse, TimeSpan scopeTimeout, EnterpriseServicesInteropOption interopOption ) { if( !TransactionManager._platformValidated ) TransactionManager.ValidatePlatform(); if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( Transaction, TimeSpan, EnterpriseServicesInteropOption )" ); } ValidateInteropOption( interopOption ); this.interopOption = interopOption; Initialize( transactionToUse, scopeTimeout, true ); if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.ctor( Transaction, TimeSpan, EnterpriseServicesInteropOption )" ); } } private bool NeedToCreateTransaction( TransactionScopeOption scopeOption ) { bool retVal = false; CommonInitialize(); // If the options specify NoTransactionNeeded, that trumps everything else. switch( scopeOption ) { case TransactionScopeOption.Suppress: expectedCurrent = null; retVal = false; break; case TransactionScopeOption.Required: expectedCurrent = this.savedCurrent; // If current is null, we need to create one. if ( null == expectedCurrent ) { retVal = true; } break; case TransactionScopeOption.RequiresNew: retVal = true; break; default: throw new ArgumentOutOfRangeException( "scopeOption" ); } return retVal; } private void Initialize( Transaction transactionToUse, TimeSpan scopeTimeout, bool interopModeSpecified ) { if ( null == transactionToUse ) { throw new ArgumentNullException( "transactionToUse" ); } ValidateScopeTimeout( "scopeTimeout", scopeTimeout ); CommonInitialize(); if ( TimeSpan.Zero != scopeTimeout ) { scopeTimer = new Timer( TransactionScope.TimerCallback, this, scopeTimeout, TimeSpan.Zero ); } this.expectedCurrent = transactionToUse; this.interopModeSpecified = interopModeSpecified; if ( DiagnosticTrace.Information ) { TransactionScopeCreatedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), expectedCurrent.TransactionTraceId, TransactionScopeResult.TransactionPassed ); } PushScope(); } // We don't have a finalizer (~TransactionScope) because all it would be able to do is try to // operate on other managed objects (the transaction), which is not safe to do because they may // already have been finalized. // FXCop wants us to dispose savedCurrent, which is a Transaction, and thus disposable. But we don't // want to do that here. We want to restore Current to that value. We do dispose the expected current. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed")] public void Dispose() { bool successful = false; if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.Dispose" ); } if ( this.disposed ) { if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.Dispose" ); } return; } // Dispose for a scope can only be called on the thread where the scope was created. if( this.scopeThread != Thread.CurrentThread ) { if ( DiagnosticTrace.Error ) { InvalidOperationExceptionTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), SR.GetString( SR.InvalidScopeThread ) ); } throw new InvalidOperationException( SR.GetString( SR.InvalidScopeThread )); } Exception exToThrow = null; try { // Single threaded from this point this.disposed = true; // First, lets pop the "stack" of TransactionScopes and dispose each one that is above us in // the stack, making sure they are NOT consistent before disposing them. // Optimize the first lookup by getting both the actual current scope and actual current // transaction at the same time. TransactionScope actualCurrentScope = this.threadContextData.CurrentScope; Transaction contextTransaction = null; Transaction current = Transaction.FastGetTransaction( actualCurrentScope, this.threadContextData, out contextTransaction ); if( !Equals( actualCurrentScope )) { // Ok this is bad. But just how bad is it. The worst case scenario is that someone is // poping scopes out of order and has placed a new transaction in the top level scope. // Check for that now. if( actualCurrentScope == null ) { // Something must have gone wrong trying to clean up a bad scope // stack previously. // Make a best effort to abort the active transaction. Transaction rollbackTransaction = this.committableTransaction; if( rollbackTransaction == null ) { rollbackTransaction = this.dependentTransaction; } Debug.Assert( rollbackTransaction != null ); rollbackTransaction.Rollback(); successful = true; throw TransactionException.CreateInvalidOperationException( SR.GetString( SR.TraceSourceBase ), SR.GetString( SR.TransactionScopeInvalidNesting ), null ); } // Verify that expectedCurrent is the same as the "current" current if we the interopOption value is None. else if( EnterpriseServicesInteropOption.None == actualCurrentScope.interopOption ) { if ( ( ( null != actualCurrentScope.expectedCurrent ) && ( ! actualCurrentScope.expectedCurrent.Equals( current ) ) ) || ( ( null != current ) && ( null == actualCurrentScope.expectedCurrent ) ) ) { if ( DiagnosticTrace.Warning ) { TransactionTraceIdentifier myId; TransactionTraceIdentifier currentId; if ( null == current ) { currentId = TransactionTraceIdentifier.Empty; } else { currentId = current.TransactionTraceId; } if ( null == this.expectedCurrent ) { myId = TransactionTraceIdentifier.Empty; } else { myId = this.expectedCurrent.TransactionTraceId; } TransactionScopeCurrentChangedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), myId, currentId ); } exToThrow = TransactionException.CreateInvalidOperationException( SR.GetString( SR.TraceSourceBase ), SR.GetString( SR.TransactionScopeIncorrectCurrent ), null ); // If there is a current transaction, abort it. if ( null != current ) { try { current.Rollback(); } catch ( TransactionException ) { // we are already going to throw and exception, so just ignore this one. } catch ( ObjectDisposedException ) { // Dito } } } } // Now fix up the scopes while ( !Equals( actualCurrentScope )) { if ( null == exToThrow ) { exToThrow = TransactionException.CreateInvalidOperationException( SR.GetString( SR.TraceSourceBase ), SR.GetString( SR.TransactionScopeInvalidNesting ), null ); } if ( DiagnosticTrace.Warning ) { if ( null == actualCurrentScope.expectedCurrent ) { TransactionScopeNestedIncorrectlyTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), TransactionTraceIdentifier.Empty ); } else { TransactionScopeNestedIncorrectlyTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), actualCurrentScope.expectedCurrent.TransactionTraceId ); } } actualCurrentScope.complete = false; try { actualCurrentScope.InternalDispose(); } catch ( TransactionException ) { // we are already going to throw an exception, so just ignore this one. } actualCurrentScope = this.threadContextData.CurrentScope; // We want to fail this scope, too, because work may have been done in one of these other // nested scopes that really should have been done in my scope. this.complete = false; } } else { // Verify that expectedCurrent is the same as the "current" current if we the interopOption value is None. // If we got here, actualCurrentScope is the same as "this". if( EnterpriseServicesInteropOption.None == this.interopOption ) { if ((( null != this.expectedCurrent ) && ( ! this.expectedCurrent.Equals( current ))) || (( null != current ) && ( null == this.expectedCurrent )) ) { if ( DiagnosticTrace.Warning ) { TransactionTraceIdentifier myId; TransactionTraceIdentifier currentId; if ( null == current ) { currentId = TransactionTraceIdentifier.Empty; } else { currentId = current.TransactionTraceId; } if ( null == this.expectedCurrent ) { myId = TransactionTraceIdentifier.Empty; } else { myId = this.expectedCurrent.TransactionTraceId; } TransactionScopeCurrentChangedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), myId, currentId ); } if ( null == exToThrow ) { exToThrow = TransactionException.CreateInvalidOperationException( SR.GetString( SR.TraceSourceBase ), SR.GetString( SR.TransactionScopeIncorrectCurrent ), null ); } // If there is a current transaction, abort it. if ( null != current ) { try { current.Rollback(); } catch ( TransactionException ) { // we are already going to throw and exception, so just ignore this one. } catch ( ObjectDisposedException ) { // Dito } } // Set consistent to false so that the subsequent call to // InternalDispose below will rollback this.expectedCurrent. this.complete = false; } } } successful = true; } finally { if ( !successful ) { PopScope(); } } // No try..catch here. Just let any exception thrown by InternalDispose go out. InternalDispose(); if ( null != exToThrow ) { throw exToThrow; } if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.Dispose" ); } } private void InternalDispose() { // Set this if it is called internally. this.disposed = true; try { PopScope(); if ( DiagnosticTrace.Information ) { if ( null == this.expectedCurrent ) { TransactionScopeDisposedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), TransactionTraceIdentifier.Empty ); } else { TransactionScopeDisposedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), this.expectedCurrent.TransactionTraceId ); } } // If Transaction.Current is not null, we have work to do. Otherwise, we don't, except to replace // the previous value. if( null != this.expectedCurrent ) { if( !this.complete ) { if ( DiagnosticTrace.Warning ) { TransactionScopeIncompleteTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), this.expectedCurrent.TransactionTraceId ); } // // Note: Rollback is not called on expected current because someone could conceiveably // dispose expectedCurrent out from under the transaction scope. // Transaction rollbackTransaction = this.committableTransaction; if( rollbackTransaction == null ) { rollbackTransaction = this.dependentTransaction; } Debug.Assert( rollbackTransaction != null ); rollbackTransaction.Rollback(); } else { // If we are supposed to commit on dispose, cast to CommittableTransaction and commit it. if ( null != this.committableTransaction ) { this.committableTransaction.Commit(); } else { Debug.Assert( null != this.dependentTransaction, "null != this.dependentTransaction" ); this.dependentTransaction.Complete(); } } } } finally { if ( null != scopeTimer ) { scopeTimer.Dispose(); } if ( null != this.committableTransaction ) { this.committableTransaction.Dispose(); // If we created the committable transaction then we placed a clone in expectedCurrent // and it needs to be disposed as well. this.expectedCurrent.Dispose(); } if ( null != this.dependentTransaction ) { this.dependentTransaction.Dispose(); } } } public void Complete() { if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.Complete" ); } if( this.disposed ) { throw new ObjectDisposedException( "TransactionScope" ); } if( this.complete ) { throw TransactionException.CreateInvalidOperationException( SR.GetString( SR.TraceSourceBase ), SR.GetString(SR.DisposeScope), null); } this.complete = true; if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), "TransactionScope.Complete" ); } } private static void TimerCallback( object state ) { TransactionScope scope = state as TransactionScope; if ( null == scope ) { if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), SR.GetString( SR.TransactionScopeTimerObjectInvalid ) ); } throw TransactionException.Create( SR.GetString( SR.TraceSourceBase ), SR.GetString( SR.InternalError) + SR.GetString( SR.TransactionScopeTimerObjectInvalid ), null ); } scope.Timeout(); } private void Timeout() { if( ( !this.complete ) && ( null != this.expectedCurrent ) ) { if ( DiagnosticTrace.Warning ) { TransactionScopeTimeoutTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), this.expectedCurrent.TransactionTraceId ); } try { this.expectedCurrent.Rollback(); } catch( ObjectDisposedException ex ) { // Tolerate the fact that the transaction has already been disposed. if ( DiagnosticTrace.Verbose ) { ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), ex ); } } catch ( TransactionException txEx ) { // Tolerate transaction exceptions - VSWhidbey 466868. if ( DiagnosticTrace.Verbose ) { ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceBase ), txEx ); } } } } private void CommonInitialize() { this.complete = false; this.dependentTransaction = null; this.disposed = false; this.committableTransaction = null; this.expectedCurrent = null; this.scopeTimer = null; this.scopeThread = Thread.CurrentThread; Transaction.GetCurrentTransactionAndScope( out this.savedCurrent, out this.savedCurrentScope, out this.threadContextData, out this.contextTransaction ); } // PushScope // // Push a transaction scope onto the stack. private void PushScope() { // Fixup the interop mode before we set current. if( !this.interopModeSpecified ) { // Transaction.InteropMode will take the interop mode on // for the scope in currentScope into account. this.interopOption = Transaction.InteropMode(this.savedCurrentScope); } // This call needs to be done first SetCurrent( expectedCurrent ); this.threadContextData.CurrentScope = this; } // PopScope // // Pop the current transaction scope off the top of the stack private void PopScope() { this.threadContextData.CurrentScope = this.savedCurrentScope; RestoreCurrent(); } // SetCurrent // // Place the given value in current by whatever means necessary for interop mode. private void SetCurrent( Transaction newCurrent ) { // Keep a dependent clone of current if we don't have one and we are not committable if( this.dependentTransaction == null && this.committableTransaction == null ) { if( newCurrent != null ) { this.dependentTransaction = newCurrent.DependentClone( DependentCloneOption.RollbackIfNotComplete ); } } switch( this.interopOption ) { case EnterpriseServicesInteropOption.None: this.threadContextData.CurrentTransaction = newCurrent; break; case EnterpriseServicesInteropOption.Automatic: Transaction.VerifyEnterpriseServicesOk(); if( Transaction.UseServiceDomainForCurrent() ) { PushServiceDomain( newCurrent ); } else { this.threadContextData.CurrentTransaction = newCurrent; } break; case EnterpriseServicesInteropOption.Full: Transaction.VerifyEnterpriseServicesOk(); PushServiceDomain( newCurrent ); break; } } // PushServiceDomain // // Create a new service domain with the given transaction. [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] // PushServiceDomain will only be called if a transaction scope is created with an // EnterpriseServicesInteropOption. TransactionScope constructors that take a ESIO are protected by // a FullTrust link demand. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2116:AptcaMethodsShouldOnlyCallAptcaMethods")] private void PushServiceDomain( Transaction newCurrent ) { // If we are not changing the transaction for the current ServiceDomain then // don't call CoEnterServiceDomain if ((newCurrent != null && newCurrent.Equals( SysES.ContextUtil.SystemTransaction )) || (newCurrent == null && SysES.ContextUtil.SystemTransaction == null )) { return; } SysES.ServiceConfig serviceConfig = new SysES.ServiceConfig(); try { // If a transaction is specified place it in BYOT. Otherwise the // default transaction option for ServiceConfig is disabled. So // if this is a not supported scope it will be cleared. if( newCurrent != null ) { // To work around an SWC bug in Com+ we need to create 2 // service domains. // Com+ will by default try to inherit synchronization from // an existing context. Turn that off. serviceConfig.Synchronization = SysES.SynchronizationOption.RequiresNew; SysES.ServiceDomain.Enter( serviceConfig ); this.createdDoubleServiceDomain = true; serviceConfig.Synchronization = SysES.SynchronizationOption.Required; serviceConfig.BringYourOwnSystemTransaction = newCurrent; } SysES.ServiceDomain.Enter( serviceConfig ); this.createdServiceDomain = true; } catch( COMException e ) { if ( System.Transactions.Oletx.NativeMethods.XACT_E_NOTRANSACTION == e.ErrorCode ) { throw TransactionException.Create( SR.GetString( SR.TraceSourceBase ), SR.GetString( SR.TransactionAlreadyOver ) , e ); } throw TransactionException.Create( SR.GetString( SR.TraceSourceBase ), e.Message, e ); } finally { if( !this.createdServiceDomain ) { // If we weren't successful in creating both service domains then // make sure to exit one of them. if( this.createdDoubleServiceDomain ) { SysES.ServiceDomain.Leave(); } } } } [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] // PushServiceDomain will only be called if a transaction scope is created with an // EnterpriseServicesInteropOption. TransactionScope constructors that take a ESIO are protected by // a FullTrust link demand. Therefore JitSafeLeaveServiceDomain is not exposed directly to a partial // trust caller. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2116:AptcaMethodsShouldOnlyCallAptcaMethods")] private void JitSafeLeaveServiceDomain() { if( this.createdDoubleServiceDomain ) { // This is an ugly work around to a bug in Com+ that prevents the use of BYOT and SWC with // anything other than required synchronization. SysES.ServiceDomain.Leave(); } SysES.ServiceDomain.Leave(); } // RestoreCurrent // // Restore current to it's previous value depending on how it was changed for this scope. private void RestoreCurrent() { if( this.createdServiceDomain ) { JitSafeLeaveServiceDomain(); } // Only restore the value that was actually in the context. this.threadContextData.CurrentTransaction = this.contextTransaction; } // ValidateInteropOption // // Validate a given interop Option private void ValidateInteropOption( EnterpriseServicesInteropOption interopOption ) { if( interopOption < EnterpriseServicesInteropOption.None || interopOption > EnterpriseServicesInteropOption.Full ) { throw new ArgumentOutOfRangeException( "interopOption" ); } } // ValidateScopeTimeout // // Scope timeouts are not governed by MaxTimeout and therefore need a special validate function private void ValidateScopeTimeout( string paramName, TimeSpan scopeTimeout ) { if( scopeTimeout < TimeSpan.Zero ) { throw new ArgumentOutOfRangeException( paramName ); } } // Denotes the action to take when the scope is disposed. bool complete; internal bool ScopeComplete { get { return this.complete; } } // Storage location for the previous current transaction. Transaction savedCurrent; // To ensure that we don't restore a value for current that was // returned to us by an external entity keep the value that was actually // in TLS when the scope was created. Transaction contextTransaction; // Storage for the value to restore to current TransactionScope savedCurrentScope; // Store a reference to the context data object for this scope. ContextData threadContextData; // Store a reference to the value that this scope expects for current Transaction expectedCurrent; // Store a reference to the committable form of this transaction if // the scope made one. CommittableTransaction committableTransaction; // Store a reference to the scopes transaction guard. DependentTransaction dependentTransaction; // Note when the scope is disposed. bool disposed; // Timer scopeTimer; // Store a reference to the thread on which the scope was created so that we can // check to make sure that the dispose pattern for scope is being used correctly. Thread scopeThread; // Store a member to let us know if a new service domain has been created for this // scope. bool createdServiceDomain; bool createdDoubleServiceDomain; // Store the interop mode for this transaction scope. bool interopModeSpecified = false; EnterpriseServicesInteropOption interopOption; internal EnterpriseServicesInteropOption InteropMode { get { return this.interopOption; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ProxyHelper.cs
- FontWeights.cs
- WebPageTraceListener.cs
- AccessorTable.cs
- TypeValidationEventArgs.cs
- ItemsControlAutomationPeer.cs
- RecordConverter.cs
- ComponentCache.cs
- RepeatBehavior.cs
- ApplicationHost.cs
- _AutoWebProxyScriptHelper.cs
- NativeMethods.cs
- ExpressionList.cs
- StringFreezingAttribute.cs
- InvalidComObjectException.cs
- ApplicationServiceManager.cs
- ContextQuery.cs
- Section.cs
- PageThemeBuildProvider.cs
- Italic.cs
- Identity.cs
- TypeLoadException.cs
- ActivityBuilderHelper.cs
- FormsAuthenticationUser.cs
- MeasurementDCInfo.cs
- ExpressionBindingCollection.cs
- NumberFormatInfo.cs
- DrawingState.cs
- BroadcastEventHelper.cs
- HtmlInputHidden.cs
- PageVisual.cs
- WebRequest.cs
- DiscoveryMessageSequence.cs
- ResourceDescriptionAttribute.cs
- HMACSHA1.cs
- ReturnEventArgs.cs
- XmlSerializableReader.cs
- PropertyDescriptor.cs
- TypedRowHandler.cs
- DesignerDataTable.cs
- BufferedOutputStream.cs
- CqlLexerHelpers.cs
- PartitionResolver.cs
- LayoutUtils.cs
- EventProviderClassic.cs
- FormViewDeleteEventArgs.cs
- X509UI.cs
- SQLRoleProvider.cs
- MsmqInputSessionChannel.cs
- RegexCharClass.cs
- HwndStylusInputProvider.cs
- XmlQualifiedNameTest.cs
- TopClause.cs
- CodeAccessPermission.cs
- SelectionItemPattern.cs
- Config.cs
- ProcessHost.cs
- ServiceDescriptionReflector.cs
- StringAttributeCollection.cs
- UnitySerializationHolder.cs
- LicenseContext.cs
- SimpleType.cs
- HiddenFieldPageStatePersister.cs
- BehaviorEditorPart.cs
- ColumnReorderedEventArgs.cs
- NetSectionGroup.cs
- BaseDataList.cs
- PrintPageEvent.cs
- RoleBoolean.cs
- XmlFormatReaderGenerator.cs
- Visitors.cs
- MenuItem.cs
- XmlIterators.cs
- XslTransform.cs
- InputBinder.cs
- ClassicBorderDecorator.cs
- C14NUtil.cs
- XmlSchemaIdentityConstraint.cs
- EntityProviderFactory.cs
- log.cs
- MouseBinding.cs
- XmlReflectionImporter.cs
- ViewStateModeByIdAttribute.cs
- GZipStream.cs
- _LoggingObject.cs
- MasterPageParser.cs
- ViewEvent.cs
- HtmlInputImage.cs
- Decorator.cs
- XmlSchemaSet.cs
- TemplateControlParser.cs
- UrlPropertyAttribute.cs
- TextEmbeddedObject.cs
- Hashtable.cs
- MonthChangedEventArgs.cs
- ErrorRuntimeConfig.cs
- InputBinder.cs
- recordstate.cs
- FocusChangedEventArgs.cs
- ProjectedSlot.cs