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
- InvalidOleVariantTypeException.cs
- X509UI.cs
- CodeDelegateInvokeExpression.cs
- Keyboard.cs
- SspiSecurityToken.cs
- EntityCollection.cs
- EventMap.cs
- RawStylusInputCustomData.cs
- Part.cs
- log.cs
- ActivityAction.cs
- HMACSHA256.cs
- Int64AnimationBase.cs
- TrustManagerMoreInformation.cs
- EntityDesignerDataSourceView.cs
- LoginName.cs
- ProcessThreadCollection.cs
- NameTable.cs
- WindowsListViewSubItem.cs
- ButtonBase.cs
- StorageEntityContainerMapping.cs
- ReadWriteSpinLock.cs
- SafeHGlobalHandleCritical.cs
- DataGridViewCellConverter.cs
- Vector3DCollectionConverter.cs
- ExpressionPrefixAttribute.cs
- AtlasWeb.Designer.cs
- XmlSchemaCollection.cs
- Deflater.cs
- EventSetter.cs
- BitmapEffect.cs
- UInt32.cs
- DataRecordInternal.cs
- RectIndependentAnimationStorage.cs
- FixUpCollection.cs
- DataGridViewCellMouseEventArgs.cs
- TextParagraphView.cs
- LoaderAllocator.cs
- PeerOutputChannel.cs
- AnonymousIdentificationSection.cs
- GuidelineCollection.cs
- ObjectStateFormatter.cs
- MembershipSection.cs
- ApplicationFileCodeDomTreeGenerator.cs
- ImageDrawing.cs
- _SingleItemRequestCache.cs
- Size3D.cs
- MatcherBuilder.cs
- HelpEvent.cs
- BinaryFormatterWriter.cs
- SymbolEqualComparer.cs
- SchemaImporter.cs
- SqlBuffer.cs
- ImpersonateTokenRef.cs
- HttpServerUtilityBase.cs
- EncodingDataItem.cs
- IndicFontClient.cs
- PageCatalogPart.cs
- webeventbuffer.cs
- XmlUrlEditor.cs
- SQLByteStorage.cs
- ADMembershipProvider.cs
- SequentialWorkflowHeaderFooter.cs
- XamlToRtfWriter.cs
- SafeFileMapViewHandle.cs
- EntityCollection.cs
- PolyQuadraticBezierSegment.cs
- ResourcePool.cs
- SupportingTokenSpecification.cs
- WebPartEventArgs.cs
- DataDocumentXPathNavigator.cs
- SingleAnimation.cs
- _DigestClient.cs
- InvalidAsynchronousStateException.cs
- MessageHeader.cs
- NameSpaceExtractor.cs
- Wizard.cs
- Cursors.cs
- PathGradientBrush.cs
- PeerNameResolver.cs
- DataGridViewRowCollection.cs
- EditorZoneBase.cs
- MatrixAnimationUsingKeyFrames.cs
- InvokeGenerator.cs
- WeakReference.cs
- WebPartAddingEventArgs.cs
- OletxCommittableTransaction.cs
- COM2IVsPerPropertyBrowsingHandler.cs
- DataServiceException.cs
- ResourceManagerWrapper.cs
- Vector3DAnimationBase.cs
- TextEmbeddedObject.cs
- Bitmap.cs
- DataGridViewMethods.cs
- ByteAnimation.cs
- SingleSelectRootGridEntry.cs
- ProjectedWrapper.cs
- TableLayoutCellPaintEventArgs.cs
- LowerCaseStringConverter.cs
- DataGridCell.cs