File: System\Transactions\Transaction.cs
Project: ndp\cdf\src\NetFx20\System.Transactions\System.Transactions.csproj (System.Transactions)
//-----------------------------------------------------------------------------
// <copyright file="Transaction.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//-----------------------------------------------------------------------------
 
#define USE_ISINTRANSACTION
 
namespace System.Transactions
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Diagnostics;
    using SysES = System.EnterpriseServices;
    using System.Runtime;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    using System.Runtime.Serialization;
    using System.Runtime.Remoting.Lifetime;
    using System.Runtime.Remoting.Messaging;
    using System.Security.Permissions;
    using System.Threading;
    using System.Transactions.Diagnostics;
 
    using System.Transactions.Configuration;
 
 
    internal enum EnterpriseServicesState
    {
        Unknown = 0,
        Available = -1,
        Unavailable = 1
    }
 
 
    public class TransactionEventArgs : EventArgs
    {
        internal Transaction transaction;
        public Transaction Transaction 
        { 
            get 
            {
                return this.transaction;
            }
        }
    }
 
    public delegate void TransactionCompletedEventHandler(object sender, TransactionEventArgs e);
 
 
    public enum IsolationLevel
    {
        Serializable = 0,
        RepeatableRead = 1,
        ReadCommitted = 2,
        ReadUncommitted = 3,
        Snapshot = 4,
        Chaos = 5,
        Unspecified = 6,
    }
 
 
    public enum TransactionStatus
    {
        Active = 0,
        Committed = 1,
        Aborted = 2,
        InDoubt = 3
    }
 
 
    public enum DependentCloneOption
    {
        BlockCommitUntilComplete = 0,
        RollbackIfNotComplete = 1,
    }
 
 
    [Flags]
    public enum EnlistmentOptions
    {
        None = 0x0,
        EnlistDuringPrepareRequired = 0x1,
    }
 
 
    // When we serialize a Transaction, we specify the type OletxTransaction, so a Transaction never
    // actually gets deserialized.
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2229:ImplementSerializationConstructors")]
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2240:ImplementISerializableCorrectly")]
    [Serializable]
    public class Transaction : IDisposable, ISerializable
    {
        private static EnterpriseServicesState _enterpriseServicesOk = EnterpriseServicesState.Unknown;
        internal static bool EnterpriseServicesOk
        {
            get
            {
                if (_enterpriseServicesOk == EnterpriseServicesState.Unknown)
                {
                    if (null != Type.GetType("System.EnterpriseServices.ContextUtil, " + AssemblyRef.SystemEnterpriseServices, false))
                    {
                        _enterpriseServicesOk = EnterpriseServicesState.Available;
                    }
                    else
                    {
                        _enterpriseServicesOk = EnterpriseServicesState.Unavailable;
                    }
                }
                return (_enterpriseServicesOk == EnterpriseServicesState.Available);
            }
        }
 
 
        internal static void VerifyEnterpriseServicesOk()
        {
            if (!EnterpriseServicesOk)
            {
                throw new NotSupportedException(SR.GetString(SR.EsNotSupported));
            }
        }
 
        private static Guid IID_IObjContext = new Guid("000001c6-0000-0000-C000-000000000046");
 
        [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] 
        // Get Transaction is a non destructive action.  The transaction that is returned is given back to the 
        // calling code but that is precicely what System.Transactions is supposed to do.  The threat model 
        // for System.Transactions notes that you should be careful about the code you call when you have a
        // transaction in Current.
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2116:AptcaMethodsShouldOnlyCallAptcaMethods")]
        // The handle returned here by DangerousGetHandle is not stored anywhere so this is not a reliability issue.
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
        private static Transaction JitSafeGetContextTransaction(ContextData contextData)
        {
            // Attempt to see if we are in the default context in which case we don't need to 
            // call SystemTransaction at all.
            SafeIUnknown defaultContext = null;
 
            if (contextData.WeakDefaultComContext != null)
            {
                defaultContext = (SafeIUnknown)contextData.WeakDefaultComContext.Target;
            }
            
            if (contextData.DefaultComContextState == DefaultComContextState.Unknown ||
                (contextData.DefaultComContextState == DefaultComContextState.Available &&
                 defaultContext == null)
              )
            {
                try
                {
                    NativeMethods.CoGetDefaultContext(-1, ref IID_IObjContext, out defaultContext);
                    contextData.WeakDefaultComContext = new WeakReference(defaultContext);
                    contextData.DefaultComContextState = DefaultComContextState.Available;
                }
                catch (System.EntryPointNotFoundException e)
                {
                    if (DiagnosticTrace.Verbose)
                    {
                        ExceptionConsumedTraceRecord.Trace(SR.GetString(SR.TraceSourceBase),
                            e);
                    }
                    contextData.DefaultComContextState = DefaultComContextState.Unavailable;
                }
            }
 
            if (contextData.DefaultComContextState == DefaultComContextState.Available)
            {
                IntPtr contextToken = IntPtr.Zero;
                NativeMethods.CoGetContextToken(out contextToken);
 
                // Check to see if the context token is the default context.
                if (defaultContext.DangerousGetHandle() == contextToken)
                {
                    return null;
                }
            }
            
 
#if USE_ISINTRANSACTION
            if (!SysES.ContextUtil.IsInTransaction)
            {
                return null;
            }
#endif
 
            return (Transaction)SysES.ContextUtil.SystemTransaction;
        }
 
        
        // GetContextTransaction
        //
        // Get a transaction from Com+ through EnterpriseServices
        internal static Transaction GetContextTransaction(ContextData contextData)
        {
            if (EnterpriseServicesOk)
            {
                return JitSafeGetContextTransaction(contextData);
            }
            return null;
        }
 
 
        // UseServiceDomain
        //
        // Property tells parts of system.transactions if it should use a
        // service domain for current.
        [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] 
        // IsDefaultContext is a non destructive call and the information it provides is not exposed directly
        // to code that is calling Transaction.Current.
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2116:AptcaMethodsShouldOnlyCallAptcaMethods")]
        internal static bool UseServiceDomainForCurrent()
        {
            return !SysES.ContextUtil.IsDefaultContext();
        }
 
 
        // InteropMode
        //
        // This property figures out the current interop mode based on the
        // top of the transaction scope stack as well as the default mode
        // from config.
        internal static EnterpriseServicesInteropOption InteropMode(TransactionScope currentScope)
        {
            if (currentScope != null)
            {
                return currentScope.InteropMode;
            }
 
            return EnterpriseServicesInteropOption.None;
        }
 
 
        internal static Transaction FastGetTransaction(TransactionScope currentScope, ContextData contextData, out Transaction contextTransaction)
        {
            Transaction current = null;
            contextTransaction = null;
            
            contextTransaction = contextData.CurrentTransaction;
 
            switch (InteropMode(currentScope))
            {
                case EnterpriseServicesInteropOption.None:
 
                    current = contextTransaction;
 
                    // If there is a transaction in the execution context or if there is a current transaction scope
                    // then honer the transaction context.
                    if (current == null && currentScope == null)
                    {
                        // Otherwise check for an external current.
                        if (TransactionManager.currentDelegateSet)
                        {
                            current = TransactionManager.currentDelegate();
                        }
                        else
                        {
                            current = GetContextTransaction(contextData);
                        }
                    }
                    break;
 
                case EnterpriseServicesInteropOption.Full:
                    current = GetContextTransaction(contextData);
                    break;
 
                case EnterpriseServicesInteropOption.Automatic:
 
                    if (UseServiceDomainForCurrent())
                    {
                        current = GetContextTransaction(contextData);
                    }
                    else
                    {
                        current = contextData.CurrentTransaction;
                    }
 
                    break;
            }
 
            return current;
        }
 
 
        // GetCurrentTransactionAndScope
        //
        // Returns both the current transaction and scope.  This is implemented for optimizations
        // in TransactionScope because it is required to get both of them in several cases.
        internal static void GetCurrentTransactionAndScope(
            TxLookup defaultLookup,
            out Transaction current, 
            out TransactionScope currentScope, 
            out Transaction contextTransaction
            )
        {
            current = null;
            currentScope = null;
            contextTransaction = null;
            
            ContextData contextData = ContextData.LookupContextData(defaultLookup);
            if (contextData != null)
            {
                currentScope = contextData.CurrentScope;
                current = FastGetTransaction(currentScope, contextData, out contextTransaction);
            }
        }
 
        public static Transaction Current
        {
            get
            { 
                if (DiagnosticTrace.Verbose)
                {
                    MethodEnteredTraceRecord.Trace(SR.GetString(SR.TraceSourceBase),
                        "Transaction.get_Current"
                        );
                }
 
                Transaction current = null;                
                TransactionScope currentScope = null;
                Transaction contextValue = null;
                GetCurrentTransactionAndScope(TxLookup.Default, out current, out currentScope, out contextValue);
 
                if (currentScope != null)
                {
                    if (currentScope.ScopeComplete)
                    {
                        throw new InvalidOperationException(SR.GetString(SR.TransactionScopeComplete));
                    }
                }
 
                if (DiagnosticTrace.Verbose)
                {
                    MethodExitedTraceRecord.Trace(SR.GetString(SR.TraceSourceBase),
                        "Transaction.get_Current"
                        );
                }
 
                return current;
            }
 
            set
            {
                if (!TransactionManager._platformValidated) TransactionManager.ValidatePlatform();
 
                if (DiagnosticTrace.Verbose)
                {
                    MethodEnteredTraceRecord.Trace(SR.GetString(SR.TraceSourceBase),
                        "Transaction.set_Current"
                        );
                }
 
                // Bring your own Transaction(BYOT) is supported only for legacy scenarios. 
                // This transaction won't be flown across thread continuations.
                if (InteropMode(ContextData.TLSCurrentData.CurrentScope) != EnterpriseServicesInteropOption.None)
                {
                    if (DiagnosticTrace.Error)
                    {
                        InvalidOperationExceptionTraceRecord.Trace(SR.GetString(SR.TraceSourceBase),
                            SR.GetString(SR.CannotSetCurrent)
                            );
                    }
 
                    throw new InvalidOperationException(SR.GetString(SR.CannotSetCurrent));
                }
 
                // Support only legacy scenarios using TLS. 
                ContextData.TLSCurrentData.CurrentTransaction = value;
                // Clear CallContext data.
                CallContextCurrentData.ClearCurrentData(null, false);
 
                if (DiagnosticTrace.Verbose)
                {
                    MethodExitedTraceRecord.Trace(SR.GetString(SR.TraceSourceBase),
                        "Transaction.set_Current"
                        );
                }
            }
        }
 
        // Storage for the transaction isolation level
        internal IsolationLevel isoLevel;
 
        // Storage for the consistent flag
        internal bool complete = false;
 
        // Record an identifier for this clone
        internal int cloneId; 
 
        // Storage for a disposed flag
        internal const int disposedTrueValue = 1;
        internal int disposed = 0;
        internal bool Disposed { get { return this.disposed == Transaction.disposedTrueValue; } }
 
        internal Guid DistributedTxId
        {
            get
            {
                Guid returnValue = Guid.Empty;
 
                if (this.internalTransaction != null)
                {
                    returnValue = this.internalTransaction.DistributedTxId;
                }
                return returnValue;
            }
        }
 
        // Internal synchronization object for transactions.  It is not safe to lock on the 
        // transaction object because it is public and users of the object may lock it for 
        // other purposes.
        internal InternalTransaction internalTransaction;
 
        // The TransactionTraceIdentifier for the transaction instance.
        internal TransactionTraceIdentifier traceIdentifier;
 
        // Not used by anyone
        private Transaction() { }
 
        // Create a transaction with the given settings
        //
        internal Transaction(
            IsolationLevel isoLevel,
            InternalTransaction internalTransaction
            )
        {
            TransactionManager.ValidateIsolationLevel(isoLevel);
 
            this.isoLevel = isoLevel;
 
            // Never create a transaction with an IsolationLevel of Unspecified.
            if (IsolationLevel.Unspecified == this.isoLevel)
            {
                this.isoLevel = System.Transactions.TransactionManager.DefaultIsolationLevel;
            }
 
            if (internalTransaction != null)
            {
                this.internalTransaction = internalTransaction;
                this.cloneId = Interlocked.Increment(ref this.internalTransaction.cloneCount);
            }
            else
            {
                // Null is passed from the constructor of a CommittableTransaction.  That
                // constructor will fill in the traceIdentifier because it has allocated the
                // internal transaction.
            }
        }
 
 
        internal Transaction(
            Oletx.OletxTransaction oleTransaction
            )
        {
            this.isoLevel = oleTransaction.IsolationLevel;
            this.internalTransaction = new InternalTransaction(this, oleTransaction);
            this.cloneId = Interlocked.Increment(ref this.internalTransaction.cloneCount);
        }
 
 
        internal Transaction(
            IsolationLevel isoLevel,
            ISimpleTransactionSuperior superior
            )
        {
            TransactionManager.ValidateIsolationLevel(isoLevel);
 
            if (superior == null)
            {
                throw new ArgumentNullException("superior");
            }
 
            this.isoLevel = isoLevel;
 
            // Never create a transaction with an IsolationLevel of Unspecified.
            if (IsolationLevel.Unspecified == this.isoLevel)
            {
                this.isoLevel = System.Transactions.TransactionManager.DefaultIsolationLevel;
            }
 
            this.internalTransaction = new InternalTransaction(this, superior);
            // ISimpleTransactionSuperior is defined to also promote to MSDTC.
            this.internalTransaction.SetPromoterTypeToMSDTC();
            this.cloneId = 1;
        }
 
        #region System.Object Overrides
 
        // Don't use the identifier for the hash code.
        //
        /// <include file='doc\Transaction.uex' path='docs/doc[@for="Transaction.GetHashCode"]/*' />
        public override int GetHashCode()
        {
            return this.internalTransaction.TransactionHash;
        }
 
 
        // Don't allow equals to get the identifier
        //
        /// <include file='doc\Transaction.uex' path='docs/doc[@for="Transaction.Equals"]/*' />
        public override bool Equals(object obj)
        {
            Transaction transaction = obj as Transaction;
 
            // If we can't cast the object as a Transaction, it must not be equal
            // to this, which is a Transaction.
            if (null == transaction)
            {
                return false;
            }
 
            // Check the internal transaction object for equality.
            return this.internalTransaction.TransactionHash == transaction.internalTransaction.TransactionHash;
        }
 
        // This would be a breaking change for little benefit.
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
        public static bool operator ==(Transaction x, Transaction y) 
        {
            if (((object)x) != null)
            {
                return x.Equals(y);
            }
            return ((object)y) == null;                
        }
 
        // This would be a breaking change for little benefit.
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
        public static bool operator !=(Transaction x, Transaction y) 
        {
            if (((object)x) != null)
            {
                return !x.Equals(y);
            }
            return ((object)y) != null;
        }
 
 
        #endregion
 
        public TransactionInformation TransactionInformation
        {
            get
            {
                if (DiagnosticTrace.Verbose)
                {
                    MethodEnteredTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                        "Transaction.get_TransactionInformation"
                        );
                }
 
                if (Disposed)
                {
                    throw new ObjectDisposedException("Transaction");
                }
 
                TransactionInformation txInfo = this.internalTransaction.transactionInformation;
                if (txInfo == null)
                {
                    // A ---- would only result in an extra allocation
                    txInfo = new TransactionInformation(this.internalTransaction);
                    this.internalTransaction.transactionInformation = txInfo;
                }
 
                if (DiagnosticTrace.Verbose)
                {
                    MethodExitedTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                        "Transaction.get_TransactionInformation"
                        );
                }
 
                return txInfo;
            }
        }
 
 
        // Return the Isolation Level for the given transaction
        //
        /// <include file='doc\Transaction.uex' path='docs/doc[@for="Transaction.IsolationLevel"]/*' />
        public IsolationLevel IsolationLevel
        {
            get
            {
                if (DiagnosticTrace.Verbose)
                {
                    MethodEnteredTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                        "Transaction.get_IsolationLevel"
                        );
                }
                if (Disposed)
                {
                    throw new ObjectDisposedException("Transaction");
                }
 
                if (DiagnosticTrace.Verbose)
                {
                    MethodExitedTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                        "Transaction.get_IsolationLevel"
                        );
                }
                return this.isoLevel;
            }
        }
 
        /// <summary>
        /// Gets the PromoterType value for the transaction.
        /// </summary>
        /// <value>
        /// If the transaction has not yet been promoted and does not yet have a promotable single phase enlistment,
        /// this property value will be Guid.Empty.
        /// 
        /// If the transaction has been promoted or has a promotable single phase enlistment, this will return the
        /// promoter type specified by the transaction promoter.
        /// 
        /// If the transaction is, or will be, promoted to MSDTC, the value will be TransactionInterop.PromoterTypeDtc.
        /// </value>
        public Guid PromoterType
        {
            get
            {
                if (DiagnosticTrace.Verbose)
                {
                    MethodEnteredTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                        "Transaction.get_PromoterType"
                        );
                }
 
                if (Disposed)
                {
                    throw new ObjectDisposedException("Transaction");
                }
 
                lock (this.internalTransaction)
                {
                    return this.internalTransaction.promoterType;
                }
            }
        }
 
        /// <summary>
        /// Gets the PromotedToken for the transaction.
        /// 
        /// If the transaction has not already been promoted, retrieving this value will cause promotion. Before retrieving the
        /// PromotedToken, the Transaction.PromoterType value should be checked to see if it is a promoter type (Guid) that the
        /// caller understands. If the caller does not recognize the PromoterType value, retreiving the PromotedToken doesn't
        /// have much value because the caller doesn't know how to utilize it. But if the PromoterType is recognized, the
        /// caller should know how to utilize the PromotedToken to communicate with the promoting distributed transaction
        /// coordinator to enlist on the distributed transaction.
        /// 
        /// If the value of a transaction's PromoterType is TransactionInterop.PromoterTypeDtc, then that transaction's
        /// PromotedToken will be an MSDTC-based TransmitterPropagationToken.
        /// </summary>
        /// <returns> 
        /// The byte[] that can be used to enlist with the distributed transaction coordinator used to promote the transaction.
        /// The format of the byte[] depends upon the value of Transaction.PromoterType.
        /// </returns>
        [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name = "FullTrust")]
        public byte[] GetPromotedToken()
        {
            // We need to ask the current transaction state for the PromotedToken because depending on the state
            // we may need to induce a promotion.
            if (DiagnosticTrace.Verbose)
            {
                MethodEnteredTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    "Transaction.GetPromotedToken"
                    );
            }
 
            if (Disposed)
            {
                throw new ObjectDisposedException("Transaction");
            }
 
            // We always make a copy of the promotedToken stored in the internal transaction.
            byte[] internalPromotedToken;
            lock (this.internalTransaction)
            {
                internalPromotedToken = this.internalTransaction.State.PromotedToken(this.internalTransaction);
            }
 
            byte[] toReturn = new byte[internalPromotedToken.Length];
            Array.Copy(internalPromotedToken, toReturn, toReturn.Length);
            return toReturn;
        }
 
        [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name = "FullTrust")]
        public Enlistment EnlistDurable(
            Guid resourceManagerIdentifier, 
            IEnlistmentNotification enlistmentNotification,
            EnlistmentOptions enlistmentOptions
            )
        {
            if (DiagnosticTrace.Verbose)
            {
                MethodEnteredTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    "Transaction.EnlistDurable( IEnlistmentNotification )"
                    );
            }
 
            if (Disposed)
            {
                throw new ObjectDisposedException("Transaction");
            }
 
            if (resourceManagerIdentifier == Guid.Empty)
            {
                throw new ArgumentException(SR.GetString(SR.BadResourceManagerId), "resourceManagerIdentifier");
            }
 
            if (enlistmentNotification == null)
            {
                throw new ArgumentNullException("enlistmentNotification");
            }
 
            if (enlistmentOptions != EnlistmentOptions.None && enlistmentOptions != EnlistmentOptions.EnlistDuringPrepareRequired)
            {
                throw new ArgumentOutOfRangeException("enlistmentOptions");
            }
 
            if (this.complete)
            {
                throw TransactionException.CreateTransactionCompletedException(SR.GetString(SR.TraceSourceLtm), this.DistributedTxId);
            }
 
            lock (this.internalTransaction)
            {
                Enlistment enlistment = this.internalTransaction.State.EnlistDurable(this.internalTransaction, 
                    resourceManagerIdentifier, enlistmentNotification, enlistmentOptions, this);
 
                if (DiagnosticTrace.Verbose)
                {
                    MethodExitedTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                        "Transaction.EnlistDurable( IEnlistmentNotification )"
                        );
                }
                return enlistment;
            }
        }
 
 
        // Forward request to the state machine to take the appropriate action.
        //
        /// <include file='doc\Transaction' path='docs/doc[@for="Transaction.EnlistDurable"]/*' />
        [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name = "FullTrust")]
        public Enlistment EnlistDurable(
            Guid resourceManagerIdentifier, 
            ISinglePhaseNotification singlePhaseNotification,
            EnlistmentOptions enlistmentOptions
            )
        {
            if (DiagnosticTrace.Verbose)
            {
                MethodEnteredTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    "Transaction.EnlistDurable( ISinglePhaseNotification )"
                    );
            }
 
            if (Disposed)
            {
                throw new ObjectDisposedException("Transaction");
            }
 
            if (resourceManagerIdentifier == Guid.Empty)
            {
                throw new ArgumentException(SR.GetString(SR.BadResourceManagerId), "resourceManagerIdentifier");
            }
 
            if (singlePhaseNotification == null)
            {
                throw new ArgumentNullException("singlePhaseNotification");
            }
 
            if (enlistmentOptions != EnlistmentOptions.None && enlistmentOptions != EnlistmentOptions.EnlistDuringPrepareRequired)
            {
                throw new ArgumentOutOfRangeException("enlistmentOptions");
            }
 
            if (this.complete)
            {
                throw TransactionException.CreateTransactionCompletedException(SR.GetString(SR.TraceSourceLtm), this.DistributedTxId);
            }
 
            lock (this.internalTransaction)
            {
                Enlistment enlistment = this.internalTransaction.State.EnlistDurable(this.internalTransaction,
                    resourceManagerIdentifier, singlePhaseNotification, enlistmentOptions, this);
 
                if (DiagnosticTrace.Verbose)
                {
                    MethodExitedTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                        "Transaction.EnlistDurable( ISinglePhaseNotification )"
                        );
                }
                return enlistment;
            }
        }
 
 
        public void Rollback()
        {
            if (DiagnosticTrace.Verbose)
            {
                MethodEnteredTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    "Transaction.Rollback"
                    );
            }
 
            if (DiagnosticTrace.Warning)
            {
                TransactionRollbackCalledTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    this.TransactionTraceId
                    );
            }
 
            if (Disposed)
            {
                throw new ObjectDisposedException("Transaction");
            }
 
            lock (this.internalTransaction)
            {
                Debug.Assert(this.internalTransaction.State != null);
                this.internalTransaction.State.Rollback(this.internalTransaction, null);
            }
 
            if (DiagnosticTrace.Verbose)
            {
                MethodExitedTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    "Transaction.Rollback"
                    );
            }
        }
 
 
        // Changing the e paramater name would be a breaking change for little benefit.
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
        public void Rollback(Exception e)
        {
            if (DiagnosticTrace.Verbose)
            {
                MethodEnteredTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    "Transaction.Rollback"
                    );
            }
 
            if (DiagnosticTrace.Warning)
            {
                TransactionRollbackCalledTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    this.TransactionTraceId
                    );
            }
 
            if (Disposed)
            {
                throw new ObjectDisposedException("Transaction");
            }
 
            lock (this.internalTransaction)
            {
                Debug.Assert(this.internalTransaction.State != null);
                this.internalTransaction.State.Rollback(this.internalTransaction, e);
            }
 
            if (DiagnosticTrace.Verbose)
            {
                MethodExitedTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    "Transaction.Rollback"
                    );
            }
        }
 
 
        // Forward request to the state machine to take the appropriate action.
        //
        /// <include file='doc\Transaction' path='docs/doc[@for="Transaction.EnlistVolatile"]/*' />
        public Enlistment EnlistVolatile(
            IEnlistmentNotification enlistmentNotification,
            EnlistmentOptions enlistmentOptions
            )
        {
            if (DiagnosticTrace.Verbose)
            {
                MethodEnteredTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    "Transaction.EnlistVolatile( IEnlistmentNotification )"
                    );
            }
 
            if (Disposed)
            {
                throw new ObjectDisposedException("Transaction");
            }
 
            if (enlistmentNotification == null)
            {
                throw new ArgumentNullException("enlistmentNotification");
            }
 
            if (enlistmentOptions != EnlistmentOptions.None && enlistmentOptions != EnlistmentOptions.EnlistDuringPrepareRequired)
            {
                throw new ArgumentOutOfRangeException("enlistmentOptions");
            }
 
            if (this.complete)
            {
                throw TransactionException.CreateTransactionCompletedException(SR.GetString(SR.TraceSourceLtm), this.DistributedTxId);
            }
 
            lock (this.internalTransaction)
            {
                Enlistment enlistment = this.internalTransaction.State.EnlistVolatile(this.internalTransaction,
                    enlistmentNotification, enlistmentOptions, this);
 
                if (DiagnosticTrace.Verbose)
                {
                    MethodExitedTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                        "Transaction.EnlistVolatile( IEnlistmentNotification )"
                        );
                }
                return enlistment;
            }
        }
 
 
        // Forward request to the state machine to take the appropriate action.
        //
        /// <include file='doc\Transaction' path='docs/doc[@for="Transaction.EnlistVolatile"]/*' />
        public Enlistment EnlistVolatile(
            ISinglePhaseNotification singlePhaseNotification,
            EnlistmentOptions enlistmentOptions
            )
        {
            if (DiagnosticTrace.Verbose)
            {
                MethodEnteredTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    "Transaction.EnlistVolatile( ISinglePhaseNotification )"
                    );
            }
 
            if (Disposed)
            {
                throw new ObjectDisposedException("Transaction");
            }
 
            if (singlePhaseNotification == null)
            {
                throw new ArgumentNullException("singlePhaseNotification");
            }
 
            if (enlistmentOptions != EnlistmentOptions.None && enlistmentOptions != EnlistmentOptions.EnlistDuringPrepareRequired)
            {
                throw new ArgumentOutOfRangeException("enlistmentOptions");
            }
 
            if (this.complete)
            {
                throw TransactionException.CreateTransactionCompletedException(SR.GetString(SR.TraceSourceLtm), this.DistributedTxId);
            }
 
            lock (this.internalTransaction)
            {
                Enlistment enlistment = this.internalTransaction.State.EnlistVolatile(this.internalTransaction,
                    singlePhaseNotification, enlistmentOptions, this);
 
                if (DiagnosticTrace.Verbose)
                {
                    MethodExitedTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                        "Transaction.EnlistVolatile( ISinglePhaseNotification )"
                        );
                }
                return enlistment;
            }
        }
 
 
 
        // Create a clone of the transaction that forwards requests to this object.
        //
        /// <include file='doc\Transaction.uex' path='docs/doc[@for="Transaction.Clone"]/*' />
        public Transaction Clone()
        {
            if (DiagnosticTrace.Verbose)
            {
                MethodEnteredTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    "Transaction.Clone"
                    );
            }
 
            if (Disposed)
            {
                throw new ObjectDisposedException("Transaction");
            }
 
            if (this.complete)
            {
                throw TransactionException.CreateTransactionCompletedException(SR.GetString(SR.TraceSourceLtm), this.DistributedTxId);
            }
            
            Transaction clone = InternalClone();
 
            if (DiagnosticTrace.Verbose)
            {
                MethodExitedTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    "Transaction.Clone"
                    );
            }
            return clone;
        }
 
 
        internal Transaction InternalClone()
        {
            Transaction clone = new Transaction(this.isoLevel,
                this.internalTransaction);
 
            if (DiagnosticTrace.Verbose)
            {
                CloneCreatedTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    clone.TransactionTraceId
                    );
            }
 
            return clone;
        }
 
 
        // Create a dependent clone of the transaction that forwards requests to this object.
        //
        /// <include file='doc\Transaction.uex' path='docs/doc[@for="Transaction.Clone"]/*' />
        public DependentTransaction DependentClone(
            DependentCloneOption cloneOption
            )
        {
            if (DiagnosticTrace.Verbose)
            {
                MethodEnteredTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    "Transaction.DependentClone"
                    );
            }
 
            if (cloneOption != DependentCloneOption.BlockCommitUntilComplete
                && cloneOption != DependentCloneOption.RollbackIfNotComplete)
            {
                throw new ArgumentOutOfRangeException("cloneOption");
            }
 
            if (Disposed)
            {
                throw new ObjectDisposedException("Transaction");
            }
 
            if (this.complete)
            {
                throw TransactionException.CreateTransactionCompletedException(SR.GetString(SR.TraceSourceLtm), this.DistributedTxId);
            }
 
            DependentTransaction clone = new DependentTransaction(
                this.isoLevel, this.internalTransaction, cloneOption == DependentCloneOption.BlockCommitUntilComplete);
 
            if (DiagnosticTrace.Information)
            {
                DependentCloneCreatedTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    clone.TransactionTraceId,
                    cloneOption
                    );
            }
            if (DiagnosticTrace.Verbose)
            {
                MethodExitedTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    "Transaction.DependentClone"
                    );
            }
            return clone;
        }
 
 
        internal TransactionTraceIdentifier TransactionTraceId
        {
            get
            {
                if (this.traceIdentifier == TransactionTraceIdentifier.Empty)
                {
                    lock (this.internalTransaction)
                    {
                        if (this.traceIdentifier == TransactionTraceIdentifier.Empty)
                        {
                            TransactionTraceIdentifier temp = new TransactionTraceIdentifier(
                                this.internalTransaction.TransactionTraceId.TransactionIdentifier,
                                this.cloneId);
                            Thread.MemoryBarrier();
                            this.traceIdentifier = temp;
                        }
                    }
                }
                return this.traceIdentifier;
            }
        }
 
 
        // Forward request to the state machine to take the appropriate action.
        //
        /// <include file='doc\Transaction.uex' path='docs/doc[@for="Transaction.TransactionCompleted"]/*' />
        public event TransactionCompletedEventHandler TransactionCompleted
        {
            add
            {
                if (Disposed)
                {
                    throw new ObjectDisposedException("Transaction");
                }
 
                lock (this.internalTransaction)
                {
                    // Register for completion with the inner transaction
                    this.internalTransaction.State.AddOutcomeRegistrant(this.internalTransaction, value);
                }
            }
 
            remove
            {
                lock (this.internalTransaction)
                {
                    this.internalTransaction.transactionCompletedDelegate = (TransactionCompletedEventHandler)
                        System.Delegate.Remove(this.internalTransaction.transactionCompletedDelegate, value);
                }
            }
        }
 
 
        public void Dispose()
        {
            InternalDispose();
        }
 
 
        // Handle Transaction Disposal.
        //
        /// <include file='doc\Transaction.uex' path='docs/doc[@for="Transaction.Dispose"]/*' />
        internal virtual void InternalDispose()
        {
            if (DiagnosticTrace.Verbose)
            {
                MethodEnteredTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    "IDisposable.Dispose"
                    );
            }
 
            if (Interlocked.Exchange(ref this.disposed, Transaction.disposedTrueValue) == Transaction.disposedTrueValue)
            {
                return;
            }
 
            // Attempt to clean up the internal transaction
            long remainingITx = Interlocked.Decrement(ref this.internalTransaction.cloneCount);
            if (remainingITx == 0)
            {
                this.internalTransaction.Dispose();
            }
 
            if (DiagnosticTrace.Verbose)
            {
                MethodExitedTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    "IDisposable.Dispose"
                    );
            }
        }
 
 
        // Ask the state machine for serialization info.
        //
        /// <include file='doc\Transaction.uex' path='docs/doc[@for="Transaction.GetObjectData"]/*' />
        [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
        void ISerializable.GetObjectData(
            SerializationInfo serializationInfo,
            StreamingContext context
            )
        {
            if (DiagnosticTrace.Verbose)
            {
                MethodEnteredTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    "ISerializable.GetObjectData"
                    );
            }
 
            if (Disposed)
            {
                throw new ObjectDisposedException("Transaction");
            }
 
            if (serializationInfo == null)
            {
                throw new ArgumentNullException("serializationInfo");
            }
 
            if (this.complete)
            {
                throw TransactionException.CreateTransactionCompletedException(SR.GetString(SR.TraceSourceLtm), this.DistributedTxId);
            }
 
            lock (this.internalTransaction)
            {
                this.internalTransaction.State.GetObjectData(this.internalTransaction, serializationInfo, context);
            }
 
            if (DiagnosticTrace.Information)
            {
                TransactionSerializedTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    this.TransactionTraceId
                    );
            }
 
            if (DiagnosticTrace.Verbose)
            {
                MethodExitedTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    "ISerializable.GetObjectData"
                    );
            }
        }
 
 
        /// <summary>
        /// Create a promotable single phase enlistment that promotes to MSDTC.
        /// </summary>
        /// <param name="promotableSinglePhaseNotification">The object that implements the IPromotableSinglePhaseNotification interface.</param>
        /// <returns>
        /// True if the enlistment is successful.
        /// 
        /// False if the transaction already has a durable enlistment or promotable single phase enlistment or
        /// if the transaction has already promoted. In this case, the caller will need to enlist in the transaction through other
        /// means, such as Transaction.EnlistDurable or retreive the MSDTC export cookie or propagation token to enlist with MSDTC.
        /// </returns>
        // We apparently didn't spell Promotable like FXCop thinks it should be spelled.
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
        [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name = "FullTrust")]
        public bool EnlistPromotableSinglePhase(IPromotableSinglePhaseNotification promotableSinglePhaseNotification)
        {
            return this.EnlistPromotableSinglePhase(promotableSinglePhaseNotification, TransactionInterop.PromoterTypeDtc);
        }
 
        /// <summary>
        /// Create a promotable single phase enlistment that promotes to a distributed transaction manager other than MSDTC.
        /// </summary>
        /// <param name="promotableSinglePhaseNotification">The object that implements the IPromotableSinglePhaseNotification interface.</param>
        /// <param name="promoterType">
        /// The promoter type Guid that identifies the format of the byte[] that is returned by the ITransactionPromoter.Promote
        /// call that is implemented by the IPromotableSinglePhaseNotificationObject, and thus the promoter of the transaction.
        /// </param>
        /// <returns>
        /// True if the enlistment is successful.
        /// 
        /// False if the transaction already has a durable enlistment or promotable single phase enlistment or
        /// if the transaction has already promoted. In this case, the caller will need to enlist in the transaction through other
        /// means.
        /// 
        /// If the Transaction.PromoterType matches the promoter type supported by the caller, then the
        /// Transaction.PromotedToken can be retrieved and used to enlist directly with the identified distributed transaction manager.
        ///
        /// How the enlistment is created with the distributed transaction manager identified by the Transaction.PromoterType
        /// is defined by that distributed transaction manager.
        /// </returns>
        // We apparently didn't spell Promotable like FXCop thinks it should be spelled.
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
        [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name = "FullTrust")]
        public bool EnlistPromotableSinglePhase(IPromotableSinglePhaseNotification promotableSinglePhaseNotification, Guid promoterType)
        {
            if (DiagnosticTrace.Verbose)
            {
                MethodEnteredTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    "Transaction.EnlistPromotableSinglePhase"
                    );
            }
 
            if (Disposed)
            {
                throw new ObjectDisposedException("Transaction");
            }
 
            if (promotableSinglePhaseNotification == null)
            {
                throw new ArgumentNullException("promotableSinglePhaseNotification");
            }
 
            if (promoterType == Guid.Empty)
            {
                throw new ArgumentException(SR.GetString(SR.PromoterTypeInvalid));
            }
 
            if (this.complete)
            {
                throw TransactionException.CreateTransactionCompletedException(SR.GetString(SR.TraceSourceLtm), this.DistributedTxId);
            }
 
            bool succeeded = false;
 
            lock (this.internalTransaction)
            {
                succeeded = this.internalTransaction.State.EnlistPromotableSinglePhase(this.internalTransaction, promotableSinglePhaseNotification, this, promoterType);
            }
 
            if (DiagnosticTrace.Verbose)
            {
                MethodExitedTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    "Transaction.EnlistPromotableSinglePhase"
                    );
            }
 
            return succeeded;
        }
 
 
        [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name = "FullTrust")]
        public Enlistment PromoteAndEnlistDurable(Guid resourceManagerIdentifier,
                                                  IPromotableSinglePhaseNotification promotableNotification,
                                                  ISinglePhaseNotification enlistmentNotification,
                                                  EnlistmentOptions enlistmentOptions)
        {
            if (DiagnosticTrace.Verbose)
            {
                MethodEnteredTraceRecord.Trace(SR.GetString(SR.TraceSourceOletx),
                    "Transaction.PromoteAndEnlistDurable"
                    );
            }
 
            if (Disposed)
            {
                throw new ObjectDisposedException("Transaction");
            }
 
            if (resourceManagerIdentifier == Guid.Empty)
            {
                throw new ArgumentException(SR.GetString(SR.BadResourceManagerId), "resourceManagerIdentifier");
            }
 
            if (promotableNotification == null)
            {
                throw new ArgumentNullException("promotableNotification");
            }
 
            if (enlistmentNotification == null)
            {
                throw new ArgumentNullException("enlistmentNotification");
            }
 
            if (enlistmentOptions != EnlistmentOptions.None && enlistmentOptions != EnlistmentOptions.EnlistDuringPrepareRequired)
            {
                throw new ArgumentOutOfRangeException("enlistmentOptions");
            }
 
            if (this.complete)
            {
                throw TransactionException.CreateTransactionCompletedException(SR.GetString(SR.TraceSourceLtm), this.DistributedTxId);
            }
 
            lock (this.internalTransaction)
            {
                Enlistment enlistment = this.internalTransaction.State.PromoteAndEnlistDurable(this.internalTransaction,
                    resourceManagerIdentifier, promotableNotification, enlistmentNotification, enlistmentOptions, this);
 
                if (DiagnosticTrace.Verbose)
                {
                    MethodExitedTraceRecord.Trace(SR.GetString(SR.TraceSourceOletx),
                        "Transaction.PromoteAndEnlistDurable"
                        );
                }
                return enlistment;
            }
        }
 
        [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name = "FullTrust")]
        public void SetDistributedTransactionIdentifier(IPromotableSinglePhaseNotification promotableNotification,
                                                        Guid distributedTransactionIdentifier)
        {
            if (DiagnosticTrace.Verbose)
            {
                MethodEnteredTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    "Transaction.SetDistributedTransactionIdentifier"
                    );
            }
 
            if (Disposed)
            {
                throw new ObjectDisposedException("Transaction");
            }
 
            if (promotableNotification == null)
            {
                throw new ArgumentNullException("promotableNotification");
            }
 
            if (distributedTransactionIdentifier == Guid.Empty)
            {
                throw new ArgumentException("distributedTransactionIdentifier");
            }
 
            if (this.complete)
            {
                throw TransactionException.CreateTransactionCompletedException(SR.GetString(SR.TraceSourceLtm), this.DistributedTxId);
            }
 
            lock (this.internalTransaction)
            {
                this.internalTransaction.State.SetDistributedTransactionId(this.internalTransaction,
                    promotableNotification,
                    distributedTransactionIdentifier);
 
                if (DiagnosticTrace.Verbose)
                {
                    MethodExitedTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                        "Transaction.SetDistributedTransactionIdentifier"
                        );
                }
                return;
            }
        }
 
        internal Oletx.OletxTransaction Promote()
        {
            lock (this.internalTransaction)
            {
                // This method is only called when we expect to be promoting to MSDTC.
                this.internalTransaction.ThrowIfPromoterTypeIsNotMSDTC();
                this.internalTransaction.State.Promote(this.internalTransaction);
                return this.internalTransaction.PromotedTransaction;
            }
        }
    }
 
 
    //
    // The following code & data is related to management of Transaction.Current
    //
 
    enum DefaultComContextState
    {
        Unknown = 0,
        Unavailable = -1,
        Available = 1
    }
 
 
    [System.Security.SuppressUnmanagedCodeSecurity]
    static class NativeMethods
    {
        // User code is not allowed to pass arbitrary data to either of these methods.
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage")]
        [System.Runtime.InteropServices.DllImport("Ole32"), System.Security.SuppressUnmanagedCodeSecurityAttribute()]
        internal static extern void CoGetContextToken(out IntPtr contextToken);
 
        // User code is not allowed to pass arbitrary data to either of these methods.
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage")]
        [System.Runtime.InteropServices.DllImport("Ole32"), System.Security.SuppressUnmanagedCodeSecurityAttribute()]
        internal static extern void CoGetDefaultContext(Int32 aptType, ref Guid contextInterface, out SafeIUnknown safeUnknown);
    }
 
    //
    //  The TxLookup enum is used internally to detect where the ambient context needs to be stored or looked up. 
    //  Default                  - Used internally when looking up Transaction.Current.  
    //  DefaultCallContext - Used when TransactionScope with async flow option is enabled. Internally we will use CallContext to store the ambient transaction.
    //  Default TLS            - Used for legacy/syncronous TransactionScope. Internally we will use TLS to store the ambient transaction.  
    //
    internal enum TxLookup
    {
        Default,
        DefaultCallContext,
        DefaultTLS,
    }
 
    //
    //  CallContextCurrentData holds the ambient transaction and uses CallContext and ConditionalWeakTable to track the ambient transaction.
    //  For async flow scenarios, we should not allow flowing of transaction across app domains. To prevent transaction from flowing across
    //  AppDomain/Remoting boundaries, we are using ConditionalWeakTable to hold the actual ambient transaction and store only a object reference 
    //  in CallContext. When TransactionScope is used to invoke a call across AppDomain/Remoting boundaries, only the object reference will be sent   
    //  across and not the actaul ambient transaction which is stashed away in the ConditionalWeakTable. 
    //
    internal static class CallContextCurrentData
    {
        private static string CurrentTransactionProperty = "TxCurrent_" + Guid.NewGuid().ToString();
 
        // ConditionalWeakTable is used to automatically remove the entries that are no longer referenced. This will help prevent leaks in async nested TransactionScope
        // usage and when child nested scopes are not syncronized properly. 
        static readonly ConditionalWeakTable<ContextKey, ContextData> ContextDataTable = new ConditionalWeakTable<ContextKey, ContextData>();
 
        // 
        //  Set CallContext data with the given contextKey. 
        //  return the ContextData if already present in contextDataTable, otherwise return the default value. 
        // 
        static public ContextData CreateOrGetCurrentData(ContextKey contextKey)
        {
           CallContext.LogicalSetData(CurrentTransactionProperty, contextKey);
           return ContextDataTable.GetValue(contextKey, (env) => new ContextData(true));
        }
 
        static public void ClearCurrentData(ContextKey contextKey, bool removeContextData)
        {
            // Get the current ambient CallContext.
            ContextKey key = (ContextKey)CallContext.LogicalGetData(CurrentTransactionProperty);
            if (contextKey != null || key != null)
            {
                // removeContextData flag is used for perf optimization to avoid removing from the table in certain nested TransactionScope usage. 
                if (removeContextData)
                {
                    // if context key is passed in remove that from the contextDataTable, otherwise remove the default context key. 
                    ContextDataTable.Remove(contextKey ?? key);
                }
 
                if (key != null)
                {
                    CallContext.LogicalSetData(CurrentTransactionProperty, null);
                }
            }
        }
 
        static public bool TryGetCurrentData(out ContextData currentData)
        {
            currentData = null;
            ContextKey contextKey = (ContextKey)CallContext.LogicalGetData(CurrentTransactionProperty);
            if (contextKey == null)
            {
               return false;
            }
            else
            {
               return ContextDataTable.TryGetValue(contextKey, out currentData);
            }
        }
    }
 
    //
    // MarshalByRefObject is needed for cross AppDomain scenarios where just using object will end up with a different reference when call is made across serialization boundary.
    //
    class ContextKey : MarshalByRefObject
    {
        // Set the lease lifetime according to the AppSetting Transactions:ContextKeyLeaseLifetimeInMinutes. Only change it if the value is greater than 0.
        public override object InitializeLifetimeService()
        {
            ILease lease = (ILease)base.InitializeLifetimeService();
            int leaseLifetime = AppSettings.ContextKeyRemotingLeaseLifetimeInMinutes;
            // If the specified lease lifetime is less than 0, we don't change the value from the AppDomain.
            // This is how you opt-out of this fix.
            // A specified value of 0 was filtered out when initializing the AppSettings and it was mapped to
            // the default value, treating it as if it wasn't specified.
            if ((lease != null) && (lease.CurrentState == LeaseState.Initial) && (leaseLifetime > 0))
            {
                lease.InitialLeaseTime = TimeSpan.FromMinutes(leaseLifetime);
                lease.RenewOnCallTime = TimeSpan.FromMinutes(leaseLifetime);
            }
            return lease;
        }
    }
 
    class ContextData
    {
        internal TransactionScope CurrentScope;
        internal Transaction CurrentTransaction;
 
        internal DefaultComContextState DefaultComContextState;
        internal WeakReference WeakDefaultComContext;
 
        internal bool asyncFlow;
        
        [ThreadStatic]
        private static ContextData staticData;
      
        internal ContextData(bool asyncFlow)
        {
            this.asyncFlow = asyncFlow;
        }
        
        internal static ContextData TLSCurrentData
        {
            get
            {
                ContextData data = staticData;
                if (data == null)
                {
                    data = new ContextData(false);
                    staticData = data;
                }
                
                return data;
            }
            set 
            {
                if (value == null && staticData != null)
                {
                    // set each property to null to retain one TLS ContextData copy.
                    staticData.CurrentScope = null;
                    staticData.CurrentTransaction = null;
                    staticData.DefaultComContextState = DefaultComContextState.Unknown;
                    staticData.WeakDefaultComContext = null;
                }
                else
                {
                    staticData = value;
                }
            }
        }
 
        internal static ContextData LookupContextData(TxLookup defaultLookup)
        {
            ContextData currentData = null;          
            if (CallContextCurrentData.TryGetCurrentData(out currentData))
            {
                if (currentData.CurrentScope == null && currentData.CurrentTransaction == null && defaultLookup != TxLookup.DefaultCallContext)
                {
                    // Clear Call Context Data
                    CallContextCurrentData.ClearCurrentData(null, true);
                    return ContextData.TLSCurrentData;
                }
               
                return currentData;
            }
            else
            {
                return ContextData.TLSCurrentData;                        
            }
        }
    }
}