// 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. public void Dispose() { bool successful = false; TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { etwLog.MethodEnter(TraceSourceType.TraceSourceBase, this); } if (_disposed) { if (etwLog.IsEnabled()) { etwLog.MethodExit(TraceSourceType.TraceSourceBase, this); } return; } // Dispose for a scope can only be called on the thread where the scope was created. if ((_scopeThread != Thread.CurrentThread) && !AsyncFlowEnabled) { if (etwLog.IsEnabled()) { etwLog.InvalidOperation("TransactionScope", "InvalidScopeThread"); } throw new InvalidOperationException(SR.InvalidScopeThread); } Exception exToThrow = null; try { // Single threaded from this point _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 = _threadContextData.CurrentScope; Transaction contextTransaction = null; Transaction current = Transaction.FastGetTransaction(actualCurrentScope, _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 = _committableTransaction; if (rollbackTransaction == null) { rollbackTransaction = _dependentTransaction; } Debug.Assert(rollbackTransaction != null); rollbackTransaction.Rollback(); successful = true; throw TransactionException.CreateInvalidOperationException( TraceSourceType.TraceSourceBase, SR.TransactionScopeInvalidNesting, null, rollbackTransaction.DistributedTxId); } // 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)) ) { TransactionTraceIdentifier myId; TransactionTraceIdentifier currentId; if (null == current) { currentId = TransactionTraceIdentifier.Empty; } else { currentId = current.TransactionTraceId; } if (null == _expectedCurrent) { myId = TransactionTraceIdentifier.Empty; } else { myId = _expectedCurrent.TransactionTraceId; } if (etwLog.IsEnabled()) { etwLog.TransactionScopeCurrentChanged(currentId, myId); } exToThrow = TransactionException.CreateInvalidOperationException(TraceSourceType.TraceSourceBase, SR.TransactionScopeIncorrectCurrent, null, current == null ? Guid.Empty : current.DistributedTxId); // 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(TraceSourceType.TraceSourceBase, SR.TransactionScopeInvalidNesting, null, current == null ? Guid.Empty : current.DistributedTxId); } if (null == actualCurrentScope._expectedCurrent) { if (etwLog.IsEnabled()) { etwLog.TransactionScopeNestedIncorrectly(TransactionTraceIdentifier.Empty); } } else { if (etwLog.IsEnabled()) { etwLog.TransactionScopeNestedIncorrectly(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 = _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. _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 == _interopOption) { if (((null != _expectedCurrent) && (!_expectedCurrent.Equals(current))) || ((null != current) && (null == _expectedCurrent)) ) { TransactionTraceIdentifier myId; TransactionTraceIdentifier currentId; if (null == current) { currentId = TransactionTraceIdentifier.Empty; } else { currentId = current.TransactionTraceId; } if (null == _expectedCurrent) { myId = TransactionTraceIdentifier.Empty; } else { myId = _expectedCurrent.TransactionTraceId; } if (etwLog.IsEnabled()) { etwLog.TransactionScopeCurrentChanged(currentId, myId); } if (null == exToThrow) { exToThrow = TransactionException.CreateInvalidOperationException(TraceSourceType.TraceSourceBase, SR.TransactionScopeIncorrectCurrent, null, current == null ? Guid.Empty : current.DistributedTxId); } // 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. _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 (etwLog.IsEnabled()) { etwLog.MethodExit(TraceSourceType.TraceSourceBase, this); } }
public void Dispose() { bool flag = false; if (DiagnosticTrace.Verbose) { MethodEnteredTraceRecord.Trace(System.Transactions.SR.GetString("TraceSourceBase"), "TransactionScope.Dispose"); } if (this.disposed) { if (DiagnosticTrace.Verbose) { MethodExitedTraceRecord.Trace(System.Transactions.SR.GetString("TraceSourceBase"), "TransactionScope.Dispose"); } } else { if (this.scopeThread != Thread.CurrentThread) { if (DiagnosticTrace.Error) { InvalidOperationExceptionTraceRecord.Trace(System.Transactions.SR.GetString("TraceSourceBase"), System.Transactions.SR.GetString("InvalidScopeThread")); } throw new InvalidOperationException(System.Transactions.SR.GetString("InvalidScopeThread")); } Exception exception = null; try { this.disposed = true; TransactionScope currentScope = this.threadContextData.CurrentScope; Transaction contextTransaction = null; Transaction transaction = Transaction.FastGetTransaction(currentScope, this.threadContextData, out contextTransaction); if (!this.Equals(currentScope)) { if (currentScope == null) { Transaction committableTransaction = this.committableTransaction; if (committableTransaction == null) { committableTransaction = this.dependentTransaction; } committableTransaction.Rollback(); flag = true; throw TransactionException.CreateInvalidOperationException(System.Transactions.SR.GetString("TraceSourceBase"), System.Transactions.SR.GetString("TransactionScopeInvalidNesting"), null); } if ((currentScope.interopOption == EnterpriseServicesInteropOption.None) && (((null != currentScope.expectedCurrent) && !currentScope.expectedCurrent.Equals(transaction)) || ((null != transaction) && (null == currentScope.expectedCurrent)))) { if (DiagnosticTrace.Warning) { TransactionTraceIdentifier transactionTraceId; TransactionTraceIdentifier empty; if (null == transaction) { empty = TransactionTraceIdentifier.Empty; } else { empty = transaction.TransactionTraceId; } if (null == this.expectedCurrent) { transactionTraceId = TransactionTraceIdentifier.Empty; } else { transactionTraceId = this.expectedCurrent.TransactionTraceId; } TransactionScopeCurrentChangedTraceRecord.Trace(System.Transactions.SR.GetString("TraceSourceBase"), transactionTraceId, empty); } exception = TransactionException.CreateInvalidOperationException(System.Transactions.SR.GetString("TraceSourceBase"), System.Transactions.SR.GetString("TransactionScopeIncorrectCurrent"), null); if (null != transaction) { try { transaction.Rollback(); } catch (TransactionException) { } catch (ObjectDisposedException) { } } } while (!this.Equals(currentScope)) { if (exception == null) { exception = TransactionException.CreateInvalidOperationException(System.Transactions.SR.GetString("TraceSourceBase"), System.Transactions.SR.GetString("TransactionScopeInvalidNesting"), null); } if (DiagnosticTrace.Warning) { if (null == currentScope.expectedCurrent) { TransactionScopeNestedIncorrectlyTraceRecord.Trace(System.Transactions.SR.GetString("TraceSourceBase"), TransactionTraceIdentifier.Empty); } else { TransactionScopeNestedIncorrectlyTraceRecord.Trace(System.Transactions.SR.GetString("TraceSourceBase"), currentScope.expectedCurrent.TransactionTraceId); } } currentScope.complete = false; try { currentScope.InternalDispose(); } catch (TransactionException) { } currentScope = this.threadContextData.CurrentScope; this.complete = false; } } else if ((this.interopOption == EnterpriseServicesInteropOption.None) && (((null != this.expectedCurrent) && !this.expectedCurrent.Equals(transaction)) || ((null != transaction) && (null == this.expectedCurrent)))) { if (DiagnosticTrace.Warning) { TransactionTraceIdentifier identifier; TransactionTraceIdentifier identifier2; if (null == transaction) { identifier2 = TransactionTraceIdentifier.Empty; } else { identifier2 = transaction.TransactionTraceId; } if (null == this.expectedCurrent) { identifier = TransactionTraceIdentifier.Empty; } else { identifier = this.expectedCurrent.TransactionTraceId; } TransactionScopeCurrentChangedTraceRecord.Trace(System.Transactions.SR.GetString("TraceSourceBase"), identifier, identifier2); } if (exception == null) { exception = TransactionException.CreateInvalidOperationException(System.Transactions.SR.GetString("TraceSourceBase"), System.Transactions.SR.GetString("TransactionScopeIncorrectCurrent"), null); } if (null != transaction) { try { transaction.Rollback(); } catch (TransactionException) { } catch (ObjectDisposedException) { } } this.complete = false; } flag = true; } finally { if (!flag) { this.PopScope(); } } this.InternalDispose(); if (exception != null) { throw exception; } if (DiagnosticTrace.Verbose) { MethodExitedTraceRecord.Trace(System.Transactions.SR.GetString("TraceSourceBase"), "TransactionScope.Dispose"); } } }