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
- WebPartDisplayMode.cs
- CommonDialog.cs
- COM2ComponentEditor.cs
- TreeNodeStyleCollectionEditor.cs
- formatter.cs
- ObjectListCommandCollection.cs
- OAVariantLib.cs
- RequestDescription.cs
- ControlAdapter.cs
- TextLine.cs
- WizardForm.cs
- OutputCacheProfileCollection.cs
- BrushMappingModeValidation.cs
- RC2.cs
- XmlSchemaSimpleTypeRestriction.cs
- SoapEnumAttribute.cs
- MatrixValueSerializer.cs
- XmlQueryType.cs
- _IPv6Address.cs
- NotFiniteNumberException.cs
- StackBuilderSink.cs
- PropertyTabAttribute.cs
- AttributeSetAction.cs
- AesCryptoServiceProvider.cs
- Size.cs
- ISFTagAndGuidCache.cs
- ViewKeyConstraint.cs
- PickBranchDesigner.xaml.cs
- ServiceDurableInstanceContextProvider.cs
- Geometry3D.cs
- EncoderParameter.cs
- EntityProviderServices.cs
- ResourcesGenerator.cs
- SecurityHelper.cs
- RNGCryptoServiceProvider.cs
- DbModificationClause.cs
- wmiprovider.cs
- SafeCryptoHandles.cs
- MetadataCache.cs
- XmlImplementation.cs
- ReflectionServiceProvider.cs
- ValidatorUtils.cs
- DBAsyncResult.cs
- WriteFileContext.cs
- Metadata.cs
- WindowsStatic.cs
- PenThreadPool.cs
- HMACSHA256.cs
- FacetEnabledSchemaElement.cs
- DeferredElementTreeState.cs
- AppDomainShutdownMonitor.cs
- GeneralTransform2DTo3DTo2D.cs
- HwndSource.cs
- AssemblyFilter.cs
- ProviderConnectionPointCollection.cs
- Facet.cs
- TransformerInfo.cs
- InkSerializer.cs
- CompositeDuplexElement.cs
- exports.cs
- TcpTransportManager.cs
- EventNotify.cs
- ToolStripItemTextRenderEventArgs.cs
- TdsParameterSetter.cs
- DesignerLoader.cs
- ConfigXmlSignificantWhitespace.cs
- DodSequenceMerge.cs
- TargetPerspective.cs
- WebSysDescriptionAttribute.cs
- TraceContext.cs
- RowToFieldTransformer.cs
- AnimatedTypeHelpers.cs
- KeyInstance.cs
- IisTraceListener.cs
- XomlCompilerResults.cs
- ReadOnlyCollectionBase.cs
- RelatedPropertyManager.cs
- SqlCacheDependencyDatabase.cs
- MapPathBasedVirtualPathProvider.cs
- ParallelForEach.cs
- QueryStringParameter.cs
- basecomparevalidator.cs
- EUCJPEncoding.cs
- XamlTreeBuilder.cs
- XPathChildIterator.cs
- ControlParameter.cs
- Stacktrace.cs
- XPathNode.cs
- arclist.cs
- GregorianCalendar.cs
- oledbmetadatacollectionnames.cs
- PrintPreviewControl.cs
- Hyperlink.cs
- Accessible.cs
- TreeNodeStyleCollection.cs
- MaxMessageSizeStream.cs
- Literal.cs
- ArrayTypeMismatchException.cs
- EncodingTable.cs
- TransformerTypeCollection.cs