Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / whidbey / NetFxQFE / ndp / fx / src / Data / System / Data / SqlClient / SqlDelegatedTransaction.cs / 1 / SqlDelegatedTransaction.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //[....] //[....] //----------------------------------------------------------------------------- namespace System.Data.SqlClient { using System.Data.Common; using System.Data.SqlClient; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.ConstrainedExecution; using System.Threading; using SysTx = System.Transactions; sealed internal class SqlDelegatedTransaction : SysTx.IPromotableSinglePhaseNotification { private static int _objectTypeCount; private readonly int _objectID = Interlocked.Increment(ref _objectTypeCount); internal int ObjectID { get { return _objectID; } } // WARNING!!! Multithreaded object! // Locking strategy: Any potentailly-multithreaded operation must first lock the associated connection, then // validate this object's active state. Locked activities should ONLY include Sql-transaction state altering activities // or notifications of same. Updates to the connection's association with the transaction or to the connection pool // may be initiated here AFTER the connection lock is released, but should NOT fall under this class's locking strategy. private SqlInternalConnection _connection; // the internal connection that is the root of the transaction private IsolationLevel _isolationLevel; // the IsolationLevel of the transaction we delegated to the server private SqlInternalTransaction _internalTransaction; // the SQL Server transaction we're delegating to private SysTx.Transaction _atomicTransaction; private bool _active; // Is the transaction active? internal SqlDelegatedTransaction(SqlInternalConnection connection, SysTx.Transaction tx) { Debug.Assert(null != connection, "null connection?"); _connection = connection; _atomicTransaction = tx; _active = false; SysTx.IsolationLevel systxIsolationLevel = tx.IsolationLevel; // We need to map the System.Transactions IsolationLevel to the one // that System.Data uses and communicates to SqlServer. We could // arguably do that in Initialize when the transaction is delegated, // however it is better to do this before we actually begin the process // of delegation, in case System.Transactions adds another isolation // level we don't know about -- we can throw the exception at a better // place. switch (systxIsolationLevel) { case SysTx.IsolationLevel.ReadCommitted: _isolationLevel = IsolationLevel.ReadCommitted; break; case SysTx.IsolationLevel.ReadUncommitted: _isolationLevel = IsolationLevel.ReadUncommitted; break; case SysTx.IsolationLevel.RepeatableRead: _isolationLevel = IsolationLevel.RepeatableRead; break; case SysTx.IsolationLevel.Serializable: _isolationLevel = IsolationLevel.Serializable; break; case SysTx.IsolationLevel.Snapshot: _isolationLevel = IsolationLevel.Snapshot; break; default: throw SQL.UnknownSysTxIsolationLevel(systxIsolationLevel); } } internal SysTx.Transaction Transaction { get { return _atomicTransaction; } } public void Initialize() { // if we get here, then we know for certain that we're the delegated // transaction. SqlInternalConnection connection = _connection; SqlConnection usersConnection = connection.Connection; Bid.Trace("%d#, Connection %d#, delegating transaction.\n", ObjectID, connection.ObjectID); RuntimeHelpers.PrepareConstrainedRegions(); try { #if DEBUG object initialReliabilitySlotValue = Thread.GetData(TdsParser.ReliabilitySlot); RuntimeHelpers.PrepareConstrainedRegions(); try { Thread.SetData(TdsParser.ReliabilitySlot, true); #endif //DEBUG if (connection.IsEnlistedInTransaction) { // defect first Bid.Trace(" %d#, Connection %d#, was enlisted, now defecting.\n", ObjectID, connection.ObjectID); connection.EnlistNull(); } _internalTransaction = new SqlInternalTransaction(connection, TransactionType.Delegated, null); connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Begin, null, _isolationLevel, _internalTransaction, true); // Handle case where ExecuteTran didn't produce a new transaction, but also didn't throw. if (null == connection.CurrentTransaction) { connection.DoomThisConnection(); throw ADP.InternalError(ADP.InternalErrorCode.UnknownTransactionFailure); } _active = true; #if DEBUG } finally { Thread.SetData(TdsParser.ReliabilitySlot, initialReliabilitySlotValue); } #endif //DEBUG } catch (System.OutOfMemoryException e) { usersConnection.Abort(e); throw; } catch (System.StackOverflowException e) { usersConnection.Abort(e); throw; } catch (System.Threading.ThreadAbortException e) { usersConnection.Abort(e); throw; } } internal bool IsActive { get { return _active; } } public Byte [] Promote() { // Operations that might be affected by multi-threaded use MUST be done inside the lock. // Don't read values off of the connection outside the lock unless it doesn't really matter // from an operational standpoint (i.e. logging connection's ObjectID should be fine, // but the PromotedDTCToken can change over calls. so that must be protected). SqlInternalConnection connection = GetValidConnection(); Exception promoteException; byte[] returnValue = null; SqlConnection usersConnection = connection.Connection; Bid.Trace(" %d#, Connection %d#, promoting transaction.\n", ObjectID, connection.ObjectID); RuntimeHelpers.PrepareConstrainedRegions(); try { #if DEBUG object initialReliabilitySlotValue = Thread.GetData(TdsParser.ReliabilitySlot); RuntimeHelpers.PrepareConstrainedRegions(); try { Thread.SetData(TdsParser.ReliabilitySlot, true); #endif //DEBUG lock (connection) { try { // Now that we've acquired the lock, make sure we still have valid state for this operation. ValidateActiveOnConnection(connection); connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Promote, null, IsolationLevel.Unspecified, _internalTransaction, true); returnValue = _connection.PromotedDTCToken; promoteException = null; } catch (SqlException e) { promoteException = e; ADP.TraceExceptionWithoutRethrow(e); // Doom the connection, to make sure that the transaction is // eventually rolled back. // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event connection.DoomThisConnection(); } catch (InvalidOperationException e) { promoteException = e; ADP.TraceExceptionWithoutRethrow(e); connection.DoomThisConnection(); } } #if DEBUG } finally { Thread.SetData(TdsParser.ReliabilitySlot, initialReliabilitySlotValue); } #endif //DEBUG } catch (System.OutOfMemoryException e) { usersConnection.Abort(e); throw; } catch (System.StackOverflowException e) { usersConnection.Abort(e); throw; } catch (System.Threading.ThreadAbortException e) { usersConnection.Abort(e); throw; } if (promoteException != null) { throw SQL.PromotionFailed(promoteException); } return returnValue; } // Called by transaction to initiate abort sequence public void Rollback(SysTx.SinglePhaseEnlistment enlistment) { Debug.Assert(null != enlistment, "null enlistment?"); SqlInternalConnection connection = GetValidConnection(); SqlConnection usersConnection = connection.Connection; Bid.Trace(" %d#, Connection %d#, aborting transaction.\n", ObjectID, connection.ObjectID); RuntimeHelpers.PrepareConstrainedRegions(); try { #if DEBUG object initialReliabilitySlotValue = Thread.GetData(TdsParser.ReliabilitySlot); RuntimeHelpers.PrepareConstrainedRegions(); try { Thread.SetData(TdsParser.ReliabilitySlot, true); #endif //DEBUG lock (connection) { try { // Now that we've acquired the lock, make sure we still have valid state for this operation. ValidateActiveOnConnection(connection); _active = false; // set to inactive first, doesn't matter how the execute completes, this transaction is done. _connection = null; // Set prior to ExecuteTransaction call in case this initiates a TransactionEnd event connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Rollback, null, IsolationLevel.Unspecified, _internalTransaction, true); } catch (SqlException e) { ADP.TraceExceptionWithoutRethrow(e); // Doom the connection, to make sure that the transaction is // eventually rolled back. // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event connection.DoomThisConnection(); // Unlike SinglePhaseCommit, a rollback is a rollback, regardless // of how it happens, so SysTx won't throw an exception, and we // don't want to throw an exception either, because SysTx isn't // handling it and it may create a fail fast scenario. In the end, // there is no way for us to communicate to the consumer that this // failed for more serious reasons than usual. // // This is a bit like "should you throw if Close fails", however, // it only matters when you really need to know. In that case, // we have the tracing that we're doing to fallback on for the // investigation. } catch (InvalidOperationException e) { ADP.TraceExceptionWithoutRethrow(e); connection.DoomThisConnection(); } } // it doesn't matter whether the rollback succeeded or not, we presume // that the transaction is aborted, because it will be eventually. connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); enlistment.Aborted(); #if DEBUG } finally { Thread.SetData(TdsParser.ReliabilitySlot, initialReliabilitySlotValue); } #endif //DEBUG } catch (System.OutOfMemoryException e) { usersConnection.Abort(e); throw; } catch (System.StackOverflowException e) { usersConnection.Abort(e); throw; } catch (System.Threading.ThreadAbortException e) { usersConnection.Abort(e); throw; } } // Called by the transaction to initiate commit sequence public void SinglePhaseCommit(SysTx.SinglePhaseEnlistment enlistment) { Debug.Assert(null != enlistment, "null enlistment?"); SqlInternalConnection connection = GetValidConnection(); SqlConnection usersConnection = connection.Connection; Bid.Trace(" %d#, Connection %d#, committing transaction.\n", ObjectID, connection.ObjectID); RuntimeHelpers.PrepareConstrainedRegions(); try { #if DEBUG object initialReliabilitySlotValue = Thread.GetData(TdsParser.ReliabilitySlot); RuntimeHelpers.PrepareConstrainedRegions(); try { Thread.SetData(TdsParser.ReliabilitySlot, true); #endif //DEBUG // If the connection is dooomed, we can be certain that the // transaction will eventually be rolled back, and we shouldn't // attempt to commit it. if (connection.IsConnectionDoomed) { lock (connection) { _active = false; // set to inactive first, doesn't matter how the rest completes, this transaction is done. _connection = null; } enlistment.Aborted(SQL.ConnectionDoomed()); } else { Exception commitException; lock (connection) { try { // Now that we've acquired the lock, make sure we still have valid state for this operation. ValidateActiveOnConnection(connection); _active = false; // set to inactive first, doesn't matter how the rest completes, this transaction is done. _connection = null; // Set prior to ExecuteTransaction call in case this initiates a TransactionEnd event connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Commit, null, IsolationLevel.Unspecified, _internalTransaction, true); commitException = null; } catch (SqlException e) { commitException = e; ADP.TraceExceptionWithoutRethrow(e); // Doom the connection, to make sure that the transaction is // eventually rolled back. // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event connection.DoomThisConnection(); } catch (InvalidOperationException e) { commitException = e; ADP.TraceExceptionWithoutRethrow(e); connection.DoomThisConnection(); } } if (commitException != null) { // connection.ExecuteTransaction failed with exception if (_internalTransaction.IsCommitted) { // Even though we got an exception, the transaction // was committed by the server. enlistment.Committed(); } else if (_internalTransaction.IsAborted) { // The transaction was aborted, report that to // SysTx. enlistment.Aborted(commitException); } else { // The transaction is still active, we cannot // know the state of the transaction. enlistment.InDoubt(commitException); } // We eat the exception. This is called on the SysTx // thread, not the applications thread. If we don't // eat the exception an UnhandledException will occur, // causing the process to FailFast. } connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); if (commitException == null) { // connection.ExecuteTransaction succeeded enlistment.Committed(); } } #if DEBUG } finally { Thread.SetData(TdsParser.ReliabilitySlot, initialReliabilitySlotValue); } #endif //DEBUG } catch (System.OutOfMemoryException e) { usersConnection.Abort(e); throw; } catch (System.StackOverflowException e) { usersConnection.Abort(e); throw; } catch (System.Threading.ThreadAbortException e) { usersConnection.Abort(e); throw; } } // Event notification that transaction ended. This comes from the subscription to the Transaction's // ended event via the internal connection. If it occurs without a prior Rollback or SinglePhaseCommit call, // it indicates the transaction was ended externally (generally that one the the DTC participants aborted // the transaction). internal void TransactionEnded(SysTx.Transaction transaction) { SqlInternalConnection connection = _connection; if (connection != null) { Bid.Trace(" %d#, Connection %d#, transaction completed externally.\n", ObjectID, connection.ObjectID); lock (connection) { if (_atomicTransaction.Equals(transaction)) { // No need to validate active on connection, this operation can be called on completed transactions _active = false; _connection = null; } } } } // Check for connection validity private SqlInternalConnection GetValidConnection() { SqlInternalConnection connection = _connection; if (null == connection) { throw ADP.ObjectDisposed(this); } return connection; } // Dooms connection and throws and error if not a valid, active, delegated transaction for the given // connection. Designed to be called AFTER a lock is placed on the connection, otherwise a normal return // may not be trusted. private void ValidateActiveOnConnection(SqlInternalConnection connection) { bool valid = _active && (connection == _connection) && (connection.DelegatedTransaction == this); if (!valid) { // Invalid indicates something BAAAD happened (Commit after TransactionEnded, for instance) // Doom anything remotely involved. if (null != connection) { connection.DoomThisConnection(); } if (connection != _connection && null != _connection) { _connection.DoomThisConnection(); } throw ADP.InternalError(ADP.InternalErrorCode.UnpooledObjectHasWrongOwner); // } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //[....] //[....] //----------------------------------------------------------------------------- namespace System.Data.SqlClient { using System.Data.Common; using System.Data.SqlClient; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.ConstrainedExecution; using System.Threading; using SysTx = System.Transactions; sealed internal class SqlDelegatedTransaction : SysTx.IPromotableSinglePhaseNotification { private static int _objectTypeCount; private readonly int _objectID = Interlocked.Increment(ref _objectTypeCount); internal int ObjectID { get { return _objectID; } } // WARNING!!! Multithreaded object! // Locking strategy: Any potentailly-multithreaded operation must first lock the associated connection, then // validate this object's active state. Locked activities should ONLY include Sql-transaction state altering activities // or notifications of same. Updates to the connection's association with the transaction or to the connection pool // may be initiated here AFTER the connection lock is released, but should NOT fall under this class's locking strategy. private SqlInternalConnection _connection; // the internal connection that is the root of the transaction private IsolationLevel _isolationLevel; // the IsolationLevel of the transaction we delegated to the server private SqlInternalTransaction _internalTransaction; // the SQL Server transaction we're delegating to private SysTx.Transaction _atomicTransaction; private bool _active; // Is the transaction active? internal SqlDelegatedTransaction(SqlInternalConnection connection, SysTx.Transaction tx) { Debug.Assert(null != connection, "null connection?"); _connection = connection; _atomicTransaction = tx; _active = false; SysTx.IsolationLevel systxIsolationLevel = tx.IsolationLevel; // We need to map the System.Transactions IsolationLevel to the one // that System.Data uses and communicates to SqlServer. We could // arguably do that in Initialize when the transaction is delegated, // however it is better to do this before we actually begin the process // of delegation, in case System.Transactions adds another isolation // level we don't know about -- we can throw the exception at a better // place. switch (systxIsolationLevel) { case SysTx.IsolationLevel.ReadCommitted: _isolationLevel = IsolationLevel.ReadCommitted; break; case SysTx.IsolationLevel.ReadUncommitted: _isolationLevel = IsolationLevel.ReadUncommitted; break; case SysTx.IsolationLevel.RepeatableRead: _isolationLevel = IsolationLevel.RepeatableRead; break; case SysTx.IsolationLevel.Serializable: _isolationLevel = IsolationLevel.Serializable; break; case SysTx.IsolationLevel.Snapshot: _isolationLevel = IsolationLevel.Snapshot; break; default: throw SQL.UnknownSysTxIsolationLevel(systxIsolationLevel); } } internal SysTx.Transaction Transaction { get { return _atomicTransaction; } } public void Initialize() { // if we get here, then we know for certain that we're the delegated // transaction. SqlInternalConnection connection = _connection; SqlConnection usersConnection = connection.Connection; Bid.Trace("%d#, Connection %d#, delegating transaction.\n", ObjectID, connection.ObjectID); RuntimeHelpers.PrepareConstrainedRegions(); try { #if DEBUG object initialReliabilitySlotValue = Thread.GetData(TdsParser.ReliabilitySlot); RuntimeHelpers.PrepareConstrainedRegions(); try { Thread.SetData(TdsParser.ReliabilitySlot, true); #endif //DEBUG if (connection.IsEnlistedInTransaction) { // defect first Bid.Trace(" %d#, Connection %d#, was enlisted, now defecting.\n", ObjectID, connection.ObjectID); connection.EnlistNull(); } _internalTransaction = new SqlInternalTransaction(connection, TransactionType.Delegated, null); connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Begin, null, _isolationLevel, _internalTransaction, true); // Handle case where ExecuteTran didn't produce a new transaction, but also didn't throw. if (null == connection.CurrentTransaction) { connection.DoomThisConnection(); throw ADP.InternalError(ADP.InternalErrorCode.UnknownTransactionFailure); } _active = true; #if DEBUG } finally { Thread.SetData(TdsParser.ReliabilitySlot, initialReliabilitySlotValue); } #endif //DEBUG } catch (System.OutOfMemoryException e) { usersConnection.Abort(e); throw; } catch (System.StackOverflowException e) { usersConnection.Abort(e); throw; } catch (System.Threading.ThreadAbortException e) { usersConnection.Abort(e); throw; } } internal bool IsActive { get { return _active; } } public Byte [] Promote() { // Operations that might be affected by multi-threaded use MUST be done inside the lock. // Don't read values off of the connection outside the lock unless it doesn't really matter // from an operational standpoint (i.e. logging connection's ObjectID should be fine, // but the PromotedDTCToken can change over calls. so that must be protected). SqlInternalConnection connection = GetValidConnection(); Exception promoteException; byte[] returnValue = null; SqlConnection usersConnection = connection.Connection; Bid.Trace(" %d#, Connection %d#, promoting transaction.\n", ObjectID, connection.ObjectID); RuntimeHelpers.PrepareConstrainedRegions(); try { #if DEBUG object initialReliabilitySlotValue = Thread.GetData(TdsParser.ReliabilitySlot); RuntimeHelpers.PrepareConstrainedRegions(); try { Thread.SetData(TdsParser.ReliabilitySlot, true); #endif //DEBUG lock (connection) { try { // Now that we've acquired the lock, make sure we still have valid state for this operation. ValidateActiveOnConnection(connection); connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Promote, null, IsolationLevel.Unspecified, _internalTransaction, true); returnValue = _connection.PromotedDTCToken; promoteException = null; } catch (SqlException e) { promoteException = e; ADP.TraceExceptionWithoutRethrow(e); // Doom the connection, to make sure that the transaction is // eventually rolled back. // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event connection.DoomThisConnection(); } catch (InvalidOperationException e) { promoteException = e; ADP.TraceExceptionWithoutRethrow(e); connection.DoomThisConnection(); } } #if DEBUG } finally { Thread.SetData(TdsParser.ReliabilitySlot, initialReliabilitySlotValue); } #endif //DEBUG } catch (System.OutOfMemoryException e) { usersConnection.Abort(e); throw; } catch (System.StackOverflowException e) { usersConnection.Abort(e); throw; } catch (System.Threading.ThreadAbortException e) { usersConnection.Abort(e); throw; } if (promoteException != null) { throw SQL.PromotionFailed(promoteException); } return returnValue; } // Called by transaction to initiate abort sequence public void Rollback(SysTx.SinglePhaseEnlistment enlistment) { Debug.Assert(null != enlistment, "null enlistment?"); SqlInternalConnection connection = GetValidConnection(); SqlConnection usersConnection = connection.Connection; Bid.Trace(" %d#, Connection %d#, aborting transaction.\n", ObjectID, connection.ObjectID); RuntimeHelpers.PrepareConstrainedRegions(); try { #if DEBUG object initialReliabilitySlotValue = Thread.GetData(TdsParser.ReliabilitySlot); RuntimeHelpers.PrepareConstrainedRegions(); try { Thread.SetData(TdsParser.ReliabilitySlot, true); #endif //DEBUG lock (connection) { try { // Now that we've acquired the lock, make sure we still have valid state for this operation. ValidateActiveOnConnection(connection); _active = false; // set to inactive first, doesn't matter how the execute completes, this transaction is done. _connection = null; // Set prior to ExecuteTransaction call in case this initiates a TransactionEnd event connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Rollback, null, IsolationLevel.Unspecified, _internalTransaction, true); } catch (SqlException e) { ADP.TraceExceptionWithoutRethrow(e); // Doom the connection, to make sure that the transaction is // eventually rolled back. // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event connection.DoomThisConnection(); // Unlike SinglePhaseCommit, a rollback is a rollback, regardless // of how it happens, so SysTx won't throw an exception, and we // don't want to throw an exception either, because SysTx isn't // handling it and it may create a fail fast scenario. In the end, // there is no way for us to communicate to the consumer that this // failed for more serious reasons than usual. // // This is a bit like "should you throw if Close fails", however, // it only matters when you really need to know. In that case, // we have the tracing that we're doing to fallback on for the // investigation. } catch (InvalidOperationException e) { ADP.TraceExceptionWithoutRethrow(e); connection.DoomThisConnection(); } } // it doesn't matter whether the rollback succeeded or not, we presume // that the transaction is aborted, because it will be eventually. connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); enlistment.Aborted(); #if DEBUG } finally { Thread.SetData(TdsParser.ReliabilitySlot, initialReliabilitySlotValue); } #endif //DEBUG } catch (System.OutOfMemoryException e) { usersConnection.Abort(e); throw; } catch (System.StackOverflowException e) { usersConnection.Abort(e); throw; } catch (System.Threading.ThreadAbortException e) { usersConnection.Abort(e); throw; } } // Called by the transaction to initiate commit sequence public void SinglePhaseCommit(SysTx.SinglePhaseEnlistment enlistment) { Debug.Assert(null != enlistment, "null enlistment?"); SqlInternalConnection connection = GetValidConnection(); SqlConnection usersConnection = connection.Connection; Bid.Trace(" %d#, Connection %d#, committing transaction.\n", ObjectID, connection.ObjectID); RuntimeHelpers.PrepareConstrainedRegions(); try { #if DEBUG object initialReliabilitySlotValue = Thread.GetData(TdsParser.ReliabilitySlot); RuntimeHelpers.PrepareConstrainedRegions(); try { Thread.SetData(TdsParser.ReliabilitySlot, true); #endif //DEBUG // If the connection is dooomed, we can be certain that the // transaction will eventually be rolled back, and we shouldn't // attempt to commit it. if (connection.IsConnectionDoomed) { lock (connection) { _active = false; // set to inactive first, doesn't matter how the rest completes, this transaction is done. _connection = null; } enlistment.Aborted(SQL.ConnectionDoomed()); } else { Exception commitException; lock (connection) { try { // Now that we've acquired the lock, make sure we still have valid state for this operation. ValidateActiveOnConnection(connection); _active = false; // set to inactive first, doesn't matter how the rest completes, this transaction is done. _connection = null; // Set prior to ExecuteTransaction call in case this initiates a TransactionEnd event connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Commit, null, IsolationLevel.Unspecified, _internalTransaction, true); commitException = null; } catch (SqlException e) { commitException = e; ADP.TraceExceptionWithoutRethrow(e); // Doom the connection, to make sure that the transaction is // eventually rolled back. // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event connection.DoomThisConnection(); } catch (InvalidOperationException e) { commitException = e; ADP.TraceExceptionWithoutRethrow(e); connection.DoomThisConnection(); } } if (commitException != null) { // connection.ExecuteTransaction failed with exception if (_internalTransaction.IsCommitted) { // Even though we got an exception, the transaction // was committed by the server. enlistment.Committed(); } else if (_internalTransaction.IsAborted) { // The transaction was aborted, report that to // SysTx. enlistment.Aborted(commitException); } else { // The transaction is still active, we cannot // know the state of the transaction. enlistment.InDoubt(commitException); } // We eat the exception. This is called on the SysTx // thread, not the applications thread. If we don't // eat the exception an UnhandledException will occur, // causing the process to FailFast. } connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); if (commitException == null) { // connection.ExecuteTransaction succeeded enlistment.Committed(); } } #if DEBUG } finally { Thread.SetData(TdsParser.ReliabilitySlot, initialReliabilitySlotValue); } #endif //DEBUG } catch (System.OutOfMemoryException e) { usersConnection.Abort(e); throw; } catch (System.StackOverflowException e) { usersConnection.Abort(e); throw; } catch (System.Threading.ThreadAbortException e) { usersConnection.Abort(e); throw; } } // Event notification that transaction ended. This comes from the subscription to the Transaction's // ended event via the internal connection. If it occurs without a prior Rollback or SinglePhaseCommit call, // it indicates the transaction was ended externally (generally that one the the DTC participants aborted // the transaction). internal void TransactionEnded(SysTx.Transaction transaction) { SqlInternalConnection connection = _connection; if (connection != null) { Bid.Trace(" %d#, Connection %d#, transaction completed externally.\n", ObjectID, connection.ObjectID); lock (connection) { if (_atomicTransaction.Equals(transaction)) { // No need to validate active on connection, this operation can be called on completed transactions _active = false; _connection = null; } } } } // Check for connection validity private SqlInternalConnection GetValidConnection() { SqlInternalConnection connection = _connection; if (null == connection) { throw ADP.ObjectDisposed(this); } return connection; } // Dooms connection and throws and error if not a valid, active, delegated transaction for the given // connection. Designed to be called AFTER a lock is placed on the connection, otherwise a normal return // may not be trusted. private void ValidateActiveOnConnection(SqlInternalConnection connection) { bool valid = _active && (connection == _connection) && (connection.DelegatedTransaction == this); if (!valid) { // Invalid indicates something BAAAD happened (Commit after TransactionEnded, for instance) // Doom anything remotely involved. if (null != connection) { connection.DoomThisConnection(); } if (connection != _connection && null != _connection) { _connection.DoomThisConnection(); } throw ADP.InternalError(ADP.InternalErrorCode.UnpooledObjectHasWrongOwner); // } } } } // 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
- SecurityKeyIdentifierClause.cs
- UnsafeNativeMethods.cs
- ColorMap.cs
- ColumnReorderedEventArgs.cs
- InstanceHandle.cs
- LoginViewDesigner.cs
- DataGridViewUtilities.cs
- MetadataFile.cs
- SimpleBitVector32.cs
- ColumnCollection.cs
- Geometry.cs
- CheckBoxAutomationPeer.cs
- ColumnMapTranslator.cs
- CanExecuteRoutedEventArgs.cs
- HotCommands.cs
- DocumentViewerHelper.cs
- ContainerSelectorBehavior.cs
- StringInfo.cs
- DateTimeParse.cs
- ListItemCollection.cs
- ThrowHelper.cs
- BamlRecordWriter.cs
- CompiledELinqQueryState.cs
- ReliableChannelFactory.cs
- WebProxyScriptElement.cs
- LocalValueEnumerator.cs
- MimeTypePropertyAttribute.cs
- XmlArrayAttribute.cs
- Config.cs
- ImageField.cs
- DataRecord.cs
- FrameworkRichTextComposition.cs
- TypeConverter.cs
- FrameworkElementFactory.cs
- Font.cs
- CapacityStreamGeometryContext.cs
- _NegoStream.cs
- XmlReaderSettings.cs
- MetadataReference.cs
- PropertyTabAttribute.cs
- PasswordBox.cs
- OracleBoolean.cs
- LayoutEvent.cs
- MessageSmuggler.cs
- AttributeUsageAttribute.cs
- coordinator.cs
- _HTTPDateParse.cs
- FaultReason.cs
- RsaSecurityKey.cs
- DocumentScope.cs
- indexingfiltermarshaler.cs
- Bezier.cs
- LightweightCodeGenerator.cs
- ResourceContainer.cs
- UniformGrid.cs
- ParagraphVisual.cs
- ScriptServiceAttribute.cs
- DateTimeUtil.cs
- SplayTreeNode.cs
- CodeObjectCreateExpression.cs
- _Events.cs
- HierarchicalDataSourceControl.cs
- NameValueConfigurationCollection.cs
- AutomationPattern.cs
- Int16KeyFrameCollection.cs
- CodeTypeDeclarationCollection.cs
- QuotedPrintableStream.cs
- DbMetaDataColumnNames.cs
- ChangesetResponse.cs
- arc.cs
- EventLogTraceListener.cs
- FamilyMap.cs
- CommandHelpers.cs
- ConsoleKeyInfo.cs
- MarginsConverter.cs
- XomlCompilerParameters.cs
- TimeSpanSecondsConverter.cs
- HtmlInputFile.cs
- TableRowCollection.cs
- WindowsListViewItemCheckBox.cs
- WebDescriptionAttribute.cs
- DeclarationUpdate.cs
- BufferBuilder.cs
- StyleXamlTreeBuilder.cs
- ProxyDataContractResolver.cs
- TextEditorTyping.cs
- Properties.cs
- SortedList.cs
- SafeUserTokenHandle.cs
- BufferedGraphicsManager.cs
- ConstNode.cs
- TreeNodeBinding.cs
- ColumnMap.cs
- HttpServerVarsCollection.cs
- FrameSecurityDescriptor.cs
- StringBlob.cs
- ITreeGenerator.cs
- SafeTokenHandle.cs
- ListSortDescription.cs
- ListViewItemMouseHoverEvent.cs