Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / whidbey / NetFxQFE / ndp / fx / src / Data / System / Data / SqlClient / sqlinternaltransaction.cs / 1 / sqlinternaltransaction.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //[....] //[....] //----------------------------------------------------------------------------- namespace System.Data.SqlClient { using System.Data; using System.Data.Common; using System.Data.ProviderBase; using System.Data.Sql; using System.Data.SqlTypes; using System.Diagnostics; using System.Threading; internal enum TransactionState { Pending = 0, Active = 1, Aborted = 2, Committed = 3, Unknown = 4, } internal enum TransactionType { LocalFromTSQL = 1, LocalFromAPI = 2, Delegated = 3, Distributed = 4, Context = 5, // only valid in proc. }; sealed internal class SqlInternalTransaction { internal const long NullTransactionId = 0; private TransactionState _transactionState; private TransactionType _transactionType; private long _transactionId; // passed in the MARS headers private int _openResultCount; // passed in the MARS headers private SqlInternalConnection _innerConnection; private bool _disposing; // used to prevent us from throwing exceptions while we're disposing private WeakReference _parent; // weak ref to the outer transaction object; needs to be weak to allow GC to occur. private static int _objectTypeCount; // Bid counter internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); internal SqlInternalTransaction(SqlInternalConnection innerConnection, TransactionType type, SqlTransaction outerTransaction) : this(innerConnection, type, outerTransaction, NullTransactionId) { } internal SqlInternalTransaction(SqlInternalConnection innerConnection, TransactionType type, SqlTransaction outerTransaction, long transactionId) { Bid.PoolerTrace("%d#, Created for connection %d#, outer transaction %d#, Type %d\n", ObjectID, innerConnection.ObjectID, (null != outerTransaction) ? outerTransaction.ObjectID : -1, (int)type); _innerConnection = innerConnection; _transactionType = type; if (null != outerTransaction) { _parent = new WeakReference(outerTransaction); } _transactionId = transactionId; } internal bool HasParentTransaction { get { // Return true if we are an API started local transaction, or if we were a TSQL // started local transaction and were then wrapped with a parent transaction as // a result of a later API begin transaction. bool result = ( (TransactionType.LocalFromAPI == _transactionType) || (TransactionType.LocalFromTSQL == _transactionType && _parent != null) ); return result; } } internal bool IsAborted { get { return (TransactionState.Aborted == _transactionState); } } internal bool IsActive { get { return (TransactionState.Active == _transactionState); } } internal bool IsCommitted { get { return (TransactionState.Committed == _transactionState); } } internal bool IsCompleted { get { return (TransactionState.Aborted == _transactionState || TransactionState.Committed == _transactionState || TransactionState.Unknown == _transactionState); } } internal bool IsContext { get { bool result = (TransactionType.Context == _transactionType); return result; } } internal bool IsDelegated { get { bool result = (TransactionType.Delegated == _transactionType); return result; } } internal bool IsDistributed { get { bool result = (TransactionType.Distributed == _transactionType); return result; } } internal bool IsLocal { get { bool result = (TransactionType.LocalFromTSQL == _transactionType || TransactionType.LocalFromAPI == _transactionType || TransactionType.Context == _transactionType); return result; } } internal bool IsOrphaned { get { // An internal transaction is orphaned when its parent has been // reclaimed by GC. bool result; if (null == _parent) { // No parent, so we better be LocalFromTSQL. Should we even return in this case - // since it could be argued this is invalid? Debug.Assert(false, "Why are we calling IsOrphaned with no parent?"); Debug.Assert(_transactionType == TransactionType.LocalFromTSQL, "invalid state"); result = false; } else if (null == _parent.Target) { // We have an parent, but parent was GC'ed. result = true; } else { // We have an parent, and parent is alive. result = false; } return result; } } internal bool IsZombied { get { return (null == _innerConnection); } } internal int ObjectID { get { return _objectID; } } internal int OpenResultsCount { get { return _openResultCount; } } internal SqlTransaction Parent { get { SqlTransaction result = null; // Should we protect against this, since this probably is an invalid state? Debug.Assert(null != _parent, "Why are we calling Parent with no parent?"); if (null != _parent) { result = (SqlTransaction)_parent.Target; } return result; } } internal long TransactionId { get { return _transactionId; } set { Debug.Assert(NullTransactionId == _transactionId, "setting transaction cookie while one is active?"); _transactionId = value; } } internal void Activate () { _transactionState = TransactionState.Active; } private void CheckTransactionLevelAndZombie() { try { if (!IsZombied && GetServerTransactionLevel() == 0) { // If not zombied, not closed, and not in transaction, zombie. Zombie(); } } catch (Exception e) { // if (!ADP.IsCatchableExceptionType(e)) { throw; } ADP.TraceExceptionWithoutRethrow(e); Zombie(); // If exception caught when trying to check level, zombie. } } internal void CloseFromConnection() { SqlInternalConnection innerConnection = _innerConnection; Debug.Assert (innerConnection != null,"How can we be here if the connection is null?"); Bid.PoolerTrace(" %d#, Closing\n", ObjectID); bool processFinallyBlock = true; try { innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.IfRollback, null, IsolationLevel.Unspecified, null, false); } catch (Exception e) { processFinallyBlock = ADP.IsCatchableExceptionType(e); throw; } finally { Debug.Assert(null != Thread.GetData(TdsParser.ReliabilitySlot), "unreliable call to CloseFromConnection"); // you need to setup for a thread abort somewhere before you call this method if (processFinallyBlock) { // Always ensure we're zombied; Yukon will send an EnvChange that // will cause the zombie, but only if we actually go to the wire; // Sphinx and Shiloh won't send the env change, so we have to handle // them ourselves. Zombie(); } } } internal void Commit() { IntPtr hscp; Bid.ScopeEnter(out hscp, " %d#", ObjectID); if (_innerConnection.IsLockedForBulkCopy) { throw SQL.ConnectionLockedForBcpEvent(); } _innerConnection.ValidateConnectionForExecute(null); try { // If this transaction has been completed, throw exception since it is unusable. try { // COMMIT ignores transaction names, and so there is no reason to pass it anything. COMMIT // simply commits the transaction from the most recent BEGIN, nested or otherwise. _innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Commit, null, IsolationLevel.Unspecified, null, false); // SQL BU DT 291159 - perform full Zombie on pre-Yukon, but do not actually // complete internal transaction until informed by server in the case of Yukon // or later. if (!IsZombied && !_innerConnection.IsYukonOrNewer) { // Since nested transactions are no longer allowed, set flag to false. // This transaction has been completed. Zombie(); } else { ZombieParent(); } } catch (Exception e) { // if (ADP.IsCatchableExceptionType(e)) { CheckTransactionLevelAndZombie(); } throw; } } finally { Bid.ScopeLeave(ref hscp); } } internal void Completed(TransactionState transactionState) { Debug.Assert (TransactionState.Active < transactionState, "invalid transaction completion state?"); _transactionState = transactionState; Zombie(); } internal Int32 DecrementAndObtainOpenResultCount() { Int32 openResultCount = Interlocked.Decrement(ref _openResultCount); if (openResultCount < 0) { throw ADP.InvalidOperation("Internal Error: Open Result Count Exceeded"); } return openResultCount; } internal void Dispose() { this.Dispose(true); System.GC.SuppressFinalize(this); } private /*protected override*/ void Dispose(bool disposing) { Bid.PoolerTrace(" %d#, Disposing\n", ObjectID); if (disposing) { if (null != _innerConnection) { // implicitly rollback if transaction still valid _disposing = true; this.Rollback(); } } } private int GetServerTransactionLevel() { // This function is needed for those times when it is impossible to determine the server's // transaction level, unless the user's arguments were parsed - which is something we don't want // to do. An example when it is impossible to determine the level is after a rollback. // using (SqlCommand transactionLevelCommand = new SqlCommand("set @out = @@trancount", (SqlConnection)(_innerConnection.Owner))) { transactionLevelCommand.Transaction = Parent; SqlParameter parameter = new SqlParameter("@out", SqlDbType.Int); parameter.Direction = ParameterDirection.Output; transactionLevelCommand.Parameters.Add(parameter); // transactionLevelCommand.RunExecuteReader(0, RunBehavior.UntilDone, false /* returnDataStream */, ADP.GetServerTransactionLevel); return (int)parameter.Value; } } internal Int32 IncrementAndObtainOpenResultCount() { Int32 openResultCount = Interlocked.Increment(ref _openResultCount); if (openResultCount < 0) { throw ADP.InvalidOperation("Internal Error: Open Result Count Exceeded"); } return openResultCount; } internal void InitParent(SqlTransaction transaction) { Debug.Assert(_parent == null, "Why do we have a parent on InitParent?"); _parent = new WeakReference(transaction); } internal void Rollback() { IntPtr hscp; Bid.ScopeEnter(out hscp, " %d#", ObjectID); if (_innerConnection.IsLockedForBulkCopy) { throw SQL.ConnectionLockedForBcpEvent(); } _innerConnection.ValidateConnectionForExecute(null); try { try { // If no arg is given to ROLLBACK it will rollback to the outermost begin - rolling back // all nested transactions as well as the outermost transaction. _innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.IfRollback, null, IsolationLevel.Unspecified, null, false); // Since Rollback will rollback to outermost begin, no need to check // server transaction level. This transaction has been completed. Zombie(); } catch (Exception e) { // if (ADP.IsCatchableExceptionType(e)) { CheckTransactionLevelAndZombie(); if (!_disposing) { throw; } } else { throw; } } } finally { Bid.ScopeLeave(ref hscp); } } internal void Rollback(string transactionName) { IntPtr hscp; Bid.ScopeEnter(out hscp, " %d#, transactionName='%ls'", ObjectID, transactionName); if (_innerConnection.IsLockedForBulkCopy) { throw SQL.ConnectionLockedForBcpEvent(); } _innerConnection.ValidateConnectionForExecute(null); try { // ROLLBACK takes either a save point name or a transaction name. It will rollback the // transaction to either the save point with the save point name or begin with the // transacttion name. NOTE: for simplicity it is possible to give all save point names // the same name, and ROLLBACK will simply rollback to the most recent save point with the // save point name. if (ADP.IsEmpty(transactionName)) throw SQL.NullEmptyTransactionName(); try { _innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Rollback, transactionName, IsolationLevel.Unspecified, null, false); if (!IsZombied && !_innerConnection.IsYukonOrNewer) { // Check if Zombied before making round-trip to server. // Against Yukon we receive an envchange on the ExecuteTransaction above on the // parser that calls back into SqlTransaction for the Zombie() call. CheckTransactionLevelAndZombie(); } } catch (Exception e) { // if (ADP.IsCatchableExceptionType(e)) { CheckTransactionLevelAndZombie(); } throw; } } finally { Bid.ScopeLeave(ref hscp); } } internal void Save(string savePointName) { IntPtr hscp; Bid.ScopeEnter(out hscp, " %d#, savePointName='%ls'", ObjectID, savePointName); _innerConnection.ValidateConnectionForExecute(null); try { // ROLLBACK takes either a save point name or a transaction name. It will rollback the // transaction to either the save point with the save point name or begin with the // transacttion name. So, to rollback a nested transaction you must have a save point. // SAVE TRANSACTION MUST HAVE AN ARGUMENT!!! Save Transaction without an arg throws an // exception from the server. So, an overload for SaveTransaction without an arg doesn't make // sense to have. Save Transaction does not affect the transaction level. if (ADP.IsEmpty(savePointName)) throw SQL.NullEmptyTransactionName(); try { _innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Save, savePointName, IsolationLevel.Unspecified, null, false); } catch (Exception e) { // if (ADP.IsCatchableExceptionType(e)) { CheckTransactionLevelAndZombie(); } throw; } } finally { Bid.ScopeLeave(ref hscp); } } internal void Zombie() { // Called by several places in the code to ensure that the outer // transaction object has been zombied and the parser has broken // it's reference to us. // NOTE: we'll be called from the TdsParser when it gets appropriate // ENVCHANGE events that indicate the transaction has completed, however // we cannot rely upon those events occuring in the case of pre-Yukon // servers (and when we don't go to the wire because the connection // is broken) so we can also be called from the Commit/Rollback/Save // methods to handle that case as well. // There are two parts to a full zombie: // 1) Zombie parent and disconnect outer transaction from internal transaction // 2) Disconnect internal transaction from connection and parser // Number 1 needs to be done whenever a SqlTransaction object is completed. Number // 2 is only done when a transaction is actually completed. Since users can begin // transactions both in and outside of the API, and since nested begins are not actual // transactions we need to distinguish between #1 and #2. See SQL BU DT 291159 // for further details. ZombieParent(); SqlInternalConnection innerConnection = _innerConnection; _innerConnection = null; if (null != innerConnection) { innerConnection.DisconnectTransaction(this); } } private void ZombieParent() { if (null != _parent) { SqlTransaction parent = (SqlTransaction) _parent.Target; if (null != parent) { parent.Zombie(); } _parent = null; } } internal string TraceString() { return String.Format(/*IFormatProvider*/ null, "(ObjId={0}, tranId={1}, state={2}, type={3}, open={4}, disp={5}", ObjectID, _transactionId, _transactionState, _transactionType, _openResultCount, _disposing); } } } // 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; using System.Data.Common; using System.Data.ProviderBase; using System.Data.Sql; using System.Data.SqlTypes; using System.Diagnostics; using System.Threading; internal enum TransactionState { Pending = 0, Active = 1, Aborted = 2, Committed = 3, Unknown = 4, } internal enum TransactionType { LocalFromTSQL = 1, LocalFromAPI = 2, Delegated = 3, Distributed = 4, Context = 5, // only valid in proc. }; sealed internal class SqlInternalTransaction { internal const long NullTransactionId = 0; private TransactionState _transactionState; private TransactionType _transactionType; private long _transactionId; // passed in the MARS headers private int _openResultCount; // passed in the MARS headers private SqlInternalConnection _innerConnection; private bool _disposing; // used to prevent us from throwing exceptions while we're disposing private WeakReference _parent; // weak ref to the outer transaction object; needs to be weak to allow GC to occur. private static int _objectTypeCount; // Bid counter internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); internal SqlInternalTransaction(SqlInternalConnection innerConnection, TransactionType type, SqlTransaction outerTransaction) : this(innerConnection, type, outerTransaction, NullTransactionId) { } internal SqlInternalTransaction(SqlInternalConnection innerConnection, TransactionType type, SqlTransaction outerTransaction, long transactionId) { Bid.PoolerTrace("%d#, Created for connection %d#, outer transaction %d#, Type %d\n", ObjectID, innerConnection.ObjectID, (null != outerTransaction) ? outerTransaction.ObjectID : -1, (int)type); _innerConnection = innerConnection; _transactionType = type; if (null != outerTransaction) { _parent = new WeakReference(outerTransaction); } _transactionId = transactionId; } internal bool HasParentTransaction { get { // Return true if we are an API started local transaction, or if we were a TSQL // started local transaction and were then wrapped with a parent transaction as // a result of a later API begin transaction. bool result = ( (TransactionType.LocalFromAPI == _transactionType) || (TransactionType.LocalFromTSQL == _transactionType && _parent != null) ); return result; } } internal bool IsAborted { get { return (TransactionState.Aborted == _transactionState); } } internal bool IsActive { get { return (TransactionState.Active == _transactionState); } } internal bool IsCommitted { get { return (TransactionState.Committed == _transactionState); } } internal bool IsCompleted { get { return (TransactionState.Aborted == _transactionState || TransactionState.Committed == _transactionState || TransactionState.Unknown == _transactionState); } } internal bool IsContext { get { bool result = (TransactionType.Context == _transactionType); return result; } } internal bool IsDelegated { get { bool result = (TransactionType.Delegated == _transactionType); return result; } } internal bool IsDistributed { get { bool result = (TransactionType.Distributed == _transactionType); return result; } } internal bool IsLocal { get { bool result = (TransactionType.LocalFromTSQL == _transactionType || TransactionType.LocalFromAPI == _transactionType || TransactionType.Context == _transactionType); return result; } } internal bool IsOrphaned { get { // An internal transaction is orphaned when its parent has been // reclaimed by GC. bool result; if (null == _parent) { // No parent, so we better be LocalFromTSQL. Should we even return in this case - // since it could be argued this is invalid? Debug.Assert(false, "Why are we calling IsOrphaned with no parent?"); Debug.Assert(_transactionType == TransactionType.LocalFromTSQL, "invalid state"); result = false; } else if (null == _parent.Target) { // We have an parent, but parent was GC'ed. result = true; } else { // We have an parent, and parent is alive. result = false; } return result; } } internal bool IsZombied { get { return (null == _innerConnection); } } internal int ObjectID { get { return _objectID; } } internal int OpenResultsCount { get { return _openResultCount; } } internal SqlTransaction Parent { get { SqlTransaction result = null; // Should we protect against this, since this probably is an invalid state? Debug.Assert(null != _parent, "Why are we calling Parent with no parent?"); if (null != _parent) { result = (SqlTransaction)_parent.Target; } return result; } } internal long TransactionId { get { return _transactionId; } set { Debug.Assert(NullTransactionId == _transactionId, "setting transaction cookie while one is active?"); _transactionId = value; } } internal void Activate () { _transactionState = TransactionState.Active; } private void CheckTransactionLevelAndZombie() { try { if (!IsZombied && GetServerTransactionLevel() == 0) { // If not zombied, not closed, and not in transaction, zombie. Zombie(); } } catch (Exception e) { // if (!ADP.IsCatchableExceptionType(e)) { throw; } ADP.TraceExceptionWithoutRethrow(e); Zombie(); // If exception caught when trying to check level, zombie. } } internal void CloseFromConnection() { SqlInternalConnection innerConnection = _innerConnection; Debug.Assert (innerConnection != null,"How can we be here if the connection is null?"); Bid.PoolerTrace(" %d#, Closing\n", ObjectID); bool processFinallyBlock = true; try { innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.IfRollback, null, IsolationLevel.Unspecified, null, false); } catch (Exception e) { processFinallyBlock = ADP.IsCatchableExceptionType(e); throw; } finally { Debug.Assert(null != Thread.GetData(TdsParser.ReliabilitySlot), "unreliable call to CloseFromConnection"); // you need to setup for a thread abort somewhere before you call this method if (processFinallyBlock) { // Always ensure we're zombied; Yukon will send an EnvChange that // will cause the zombie, but only if we actually go to the wire; // Sphinx and Shiloh won't send the env change, so we have to handle // them ourselves. Zombie(); } } } internal void Commit() { IntPtr hscp; Bid.ScopeEnter(out hscp, " %d#", ObjectID); if (_innerConnection.IsLockedForBulkCopy) { throw SQL.ConnectionLockedForBcpEvent(); } _innerConnection.ValidateConnectionForExecute(null); try { // If this transaction has been completed, throw exception since it is unusable. try { // COMMIT ignores transaction names, and so there is no reason to pass it anything. COMMIT // simply commits the transaction from the most recent BEGIN, nested or otherwise. _innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Commit, null, IsolationLevel.Unspecified, null, false); // SQL BU DT 291159 - perform full Zombie on pre-Yukon, but do not actually // complete internal transaction until informed by server in the case of Yukon // or later. if (!IsZombied && !_innerConnection.IsYukonOrNewer) { // Since nested transactions are no longer allowed, set flag to false. // This transaction has been completed. Zombie(); } else { ZombieParent(); } } catch (Exception e) { // if (ADP.IsCatchableExceptionType(e)) { CheckTransactionLevelAndZombie(); } throw; } } finally { Bid.ScopeLeave(ref hscp); } } internal void Completed(TransactionState transactionState) { Debug.Assert (TransactionState.Active < transactionState, "invalid transaction completion state?"); _transactionState = transactionState; Zombie(); } internal Int32 DecrementAndObtainOpenResultCount() { Int32 openResultCount = Interlocked.Decrement(ref _openResultCount); if (openResultCount < 0) { throw ADP.InvalidOperation("Internal Error: Open Result Count Exceeded"); } return openResultCount; } internal void Dispose() { this.Dispose(true); System.GC.SuppressFinalize(this); } private /*protected override*/ void Dispose(bool disposing) { Bid.PoolerTrace(" %d#, Disposing\n", ObjectID); if (disposing) { if (null != _innerConnection) { // implicitly rollback if transaction still valid _disposing = true; this.Rollback(); } } } private int GetServerTransactionLevel() { // This function is needed for those times when it is impossible to determine the server's // transaction level, unless the user's arguments were parsed - which is something we don't want // to do. An example when it is impossible to determine the level is after a rollback. // using (SqlCommand transactionLevelCommand = new SqlCommand("set @out = @@trancount", (SqlConnection)(_innerConnection.Owner))) { transactionLevelCommand.Transaction = Parent; SqlParameter parameter = new SqlParameter("@out", SqlDbType.Int); parameter.Direction = ParameterDirection.Output; transactionLevelCommand.Parameters.Add(parameter); // transactionLevelCommand.RunExecuteReader(0, RunBehavior.UntilDone, false /* returnDataStream */, ADP.GetServerTransactionLevel); return (int)parameter.Value; } } internal Int32 IncrementAndObtainOpenResultCount() { Int32 openResultCount = Interlocked.Increment(ref _openResultCount); if (openResultCount < 0) { throw ADP.InvalidOperation("Internal Error: Open Result Count Exceeded"); } return openResultCount; } internal void InitParent(SqlTransaction transaction) { Debug.Assert(_parent == null, "Why do we have a parent on InitParent?"); _parent = new WeakReference(transaction); } internal void Rollback() { IntPtr hscp; Bid.ScopeEnter(out hscp, " %d#", ObjectID); if (_innerConnection.IsLockedForBulkCopy) { throw SQL.ConnectionLockedForBcpEvent(); } _innerConnection.ValidateConnectionForExecute(null); try { try { // If no arg is given to ROLLBACK it will rollback to the outermost begin - rolling back // all nested transactions as well as the outermost transaction. _innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.IfRollback, null, IsolationLevel.Unspecified, null, false); // Since Rollback will rollback to outermost begin, no need to check // server transaction level. This transaction has been completed. Zombie(); } catch (Exception e) { // if (ADP.IsCatchableExceptionType(e)) { CheckTransactionLevelAndZombie(); if (!_disposing) { throw; } } else { throw; } } } finally { Bid.ScopeLeave(ref hscp); } } internal void Rollback(string transactionName) { IntPtr hscp; Bid.ScopeEnter(out hscp, " %d#, transactionName='%ls'", ObjectID, transactionName); if (_innerConnection.IsLockedForBulkCopy) { throw SQL.ConnectionLockedForBcpEvent(); } _innerConnection.ValidateConnectionForExecute(null); try { // ROLLBACK takes either a save point name or a transaction name. It will rollback the // transaction to either the save point with the save point name or begin with the // transacttion name. NOTE: for simplicity it is possible to give all save point names // the same name, and ROLLBACK will simply rollback to the most recent save point with the // save point name. if (ADP.IsEmpty(transactionName)) throw SQL.NullEmptyTransactionName(); try { _innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Rollback, transactionName, IsolationLevel.Unspecified, null, false); if (!IsZombied && !_innerConnection.IsYukonOrNewer) { // Check if Zombied before making round-trip to server. // Against Yukon we receive an envchange on the ExecuteTransaction above on the // parser that calls back into SqlTransaction for the Zombie() call. CheckTransactionLevelAndZombie(); } } catch (Exception e) { // if (ADP.IsCatchableExceptionType(e)) { CheckTransactionLevelAndZombie(); } throw; } } finally { Bid.ScopeLeave(ref hscp); } } internal void Save(string savePointName) { IntPtr hscp; Bid.ScopeEnter(out hscp, " %d#, savePointName='%ls'", ObjectID, savePointName); _innerConnection.ValidateConnectionForExecute(null); try { // ROLLBACK takes either a save point name or a transaction name. It will rollback the // transaction to either the save point with the save point name or begin with the // transacttion name. So, to rollback a nested transaction you must have a save point. // SAVE TRANSACTION MUST HAVE AN ARGUMENT!!! Save Transaction without an arg throws an // exception from the server. So, an overload for SaveTransaction without an arg doesn't make // sense to have. Save Transaction does not affect the transaction level. if (ADP.IsEmpty(savePointName)) throw SQL.NullEmptyTransactionName(); try { _innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Save, savePointName, IsolationLevel.Unspecified, null, false); } catch (Exception e) { // if (ADP.IsCatchableExceptionType(e)) { CheckTransactionLevelAndZombie(); } throw; } } finally { Bid.ScopeLeave(ref hscp); } } internal void Zombie() { // Called by several places in the code to ensure that the outer // transaction object has been zombied and the parser has broken // it's reference to us. // NOTE: we'll be called from the TdsParser when it gets appropriate // ENVCHANGE events that indicate the transaction has completed, however // we cannot rely upon those events occuring in the case of pre-Yukon // servers (and when we don't go to the wire because the connection // is broken) so we can also be called from the Commit/Rollback/Save // methods to handle that case as well. // There are two parts to a full zombie: // 1) Zombie parent and disconnect outer transaction from internal transaction // 2) Disconnect internal transaction from connection and parser // Number 1 needs to be done whenever a SqlTransaction object is completed. Number // 2 is only done when a transaction is actually completed. Since users can begin // transactions both in and outside of the API, and since nested begins are not actual // transactions we need to distinguish between #1 and #2. See SQL BU DT 291159 // for further details. ZombieParent(); SqlInternalConnection innerConnection = _innerConnection; _innerConnection = null; if (null != innerConnection) { innerConnection.DisconnectTransaction(this); } } private void ZombieParent() { if (null != _parent) { SqlTransaction parent = (SqlTransaction) _parent.Target; if (null != parent) { parent.Zombie(); } _parent = null; } } internal string TraceString() { return String.Format(/*IFormatProvider*/ null, "(ObjId={0}, tranId={1}, state={2}, type={3}, open={4}, disp={5}", ObjectID, _transactionId, _transactionState, _transactionType, _openResultCount, _disposing); } } } // 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
- SoapIncludeAttribute.cs
- PageFunction.cs
- CapabilitiesUse.cs
- XmlSchema.cs
- FormsAuthenticationUser.cs
- UnsafeNativeMethods.cs
- FlatButtonAppearance.cs
- PostBackTrigger.cs
- MaskedTextBox.cs
- TextEditorTables.cs
- IxmlLineInfo.cs
- util.cs
- StorageAssociationTypeMapping.cs
- DefaultObjectSerializer.cs
- XslAst.cs
- CrossSiteScriptingValidation.cs
- DefaultWorkflowSchedulerService.cs
- RoutedEvent.cs
- OdbcConnectionFactory.cs
- ManagedIStream.cs
- ColumnHeader.cs
- SamlAttribute.cs
- ExecutionScope.cs
- EasingKeyFrames.cs
- WinHttpWebProxyFinder.cs
- RunInstallerAttribute.cs
- BitVector32.cs
- DateTimeStorage.cs
- SelectedDatesCollection.cs
- SelectionItemPattern.cs
- MobileTextWriter.cs
- MultipleViewProviderWrapper.cs
- SafeNativeMethodsOther.cs
- AuthenticationModulesSection.cs
- InputManager.cs
- WebPartDescription.cs
- FixedSOMTable.cs
- ValueSerializerAttribute.cs
- MsmqIntegrationSecurity.cs
- ExpandCollapsePattern.cs
- SqlBulkCopyColumnMapping.cs
- CheckedListBox.cs
- ScrollItemProviderWrapper.cs
- ScrollProperties.cs
- DateTimeSerializationSection.cs
- MatrixKeyFrameCollection.cs
- printdlgexmarshaler.cs
- FixedFlowMap.cs
- TableDetailsRow.cs
- MergePropertyDescriptor.cs
- StylusDevice.cs
- SecurityPolicyVersion.cs
- AssociatedControlConverter.cs
- WebPartCollection.cs
- DataGrid.cs
- WebBrowserNavigatingEventHandler.cs
- FileNotFoundException.cs
- SystemTcpConnection.cs
- RadioButton.cs
- SamlAction.cs
- FlowDocumentView.cs
- AppearanceEditorPart.cs
- ListViewSelectEventArgs.cs
- ExtenderProvidedPropertyAttribute.cs
- BinaryWriter.cs
- CharacterHit.cs
- PrimitiveDataContract.cs
- _Rfc2616CacheValidators.cs
- Pair.cs
- DateTimeOffsetStorage.cs
- XmlAggregates.cs
- ProfileParameter.cs
- EntityDataSourceSelectingEventArgs.cs
- HorizontalAlignConverter.cs
- PopupEventArgs.cs
- WebHttpEndpoint.cs
- XhtmlBasicControlAdapter.cs
- DBDataPermission.cs
- InstanceNotReadyException.cs
- TargetInvocationException.cs
- ResourceDisplayNameAttribute.cs
- MutexSecurity.cs
- NullableDecimalMinMaxAggregationOperator.cs
- UidPropertyAttribute.cs
- TableCellAutomationPeer.cs
- LabelInfo.cs
- PointAnimation.cs
- arc.cs
- ResXResourceSet.cs
- HtmlDocument.cs
- SymmetricSecurityProtocolFactory.cs
- SafeLibraryHandle.cs
- OpCodes.cs
- PageBuildProvider.cs
- FormParameter.cs
- SerializationException.cs
- CommaDelimitedStringAttributeCollectionConverter.cs
- HtmlDocument.cs
- CustomErrorsSection.cs
- SmiContextFactory.cs