protected override void CleanupTransactionOnCompletion(Transaction transaction) { SqlDelegatedTransaction delegatedTransaction = this.DelegatedTransaction; if (delegatedTransaction != null) { delegatedTransaction.TransactionEnded(transaction); } }
override protected void CleanupTransactionOnCompletion(Transaction transaction) { // Note: unlocked, potentially multi-threaded code, so pull delegate to local to // ensure it doesn't change between test and call. SqlDelegatedTransaction delegatedTransaction = DelegatedTransaction; if (null != delegatedTransaction) { delegatedTransaction.TransactionEnded(transaction); } }
private void TransactionEndedByServer(long transactionId) { SqlDelegatedTransaction delegatedTransaction = base.DelegatedTransaction; if (delegatedTransaction != null) { delegatedTransaction.Transaction.Rollback(); base.DelegatedTransaction = null; } this.TransactionEnded(transactionId, System.Data.SqlClient.TransactionState.Unknown); }
private void TransactionEndedByServer(long transactionId, TransactionState transactionState) { // Some extra steps required when the server initiates the ending of a transaction unilaterally // as opposed to the client initiating it. // Basically, we have to make the delegated transaction (if there is one) aware of the situation. SqlDelegatedTransaction delegatedTransaction = DelegatedTransaction; if (null != delegatedTransaction) { delegatedTransaction.Transaction.Rollback(); // just to make sure... DelegatedTransaction = null; // He's dead, Jim. } // Now handle the standard transaction-ended stuff. TransactionEnded(transactionId, transactionState); }
private void EnlistNonNull(Transaction tx) { Debug.Assert(null != tx, "null transaction?"); bool hasDelegatedTransaction = false; // Promotable transactions are only supported on Yukon // servers or newer. SqlDelegatedTransaction delegatedTransaction = new SqlDelegatedTransaction(this, tx); try { // NOTE: System.Transactions claims to resolve all // potential race conditions between multiple delegate // requests of the same transaction to different // connections in their code, such that only one // attempt to delegate will succeed. // NOTE: PromotableSinglePhaseEnlist will eventually // make a round trip to the server; doing this inside // a lock is not the best choice. We presume that you // aren't trying to enlist concurrently on two threads // and leave it at that -- We don't claim any thread // safety with regard to multiple concurrent requests // to enlist the same connection in different // transactions, which is good, because we don't have // it anyway. // PromotableSinglePhaseEnlist may not actually promote // the transaction when it is already delegated (this is // the way they resolve the race condition when two // threads attempt to delegate the same Lightweight // Transaction) In that case, we can safely ignore // our delegated transaction, and proceed to enlist // in the promoted one. // NOTE: Global Transactions is an Azure SQL DB only // feature where the Transaction Manager (TM) is not // MS-DTC. Sys.Tx added APIs to support Non MS-DTC // promoter types/TM in .NET 4.6.1. Following directions // from .NETFX shiproom, to avoid a "hard-dependency" // (compile time) on Sys.Tx, we use reflection to invoke // the new APIs. Further, the _isGlobalTransaction flag // indicates that this is an Azure SQL DB Transaction // that could be promoted to a Global Transaction (it's // always false for on-prem Sql Server). The Promote() // call in SqlDelegatedTransaction makes sure that the // right Sys.Tx.dll is loaded and that Global Transactions // are actually allowed for this Azure SQL DB. if (_isGlobalTransaction) { if (SysTxForGlobalTransactions.EnlistPromotableSinglePhase == null) { // This could be a local Azure SQL DB transaction. hasDelegatedTransaction = tx.EnlistPromotableSinglePhase(delegatedTransaction); } else { hasDelegatedTransaction = (bool)SysTxForGlobalTransactions.EnlistPromotableSinglePhase.Invoke(tx, new object[] { delegatedTransaction, _globalTransactionTMID }); } } else { // This is an MS-DTC distributed transaction hasDelegatedTransaction = tx.EnlistPromotableSinglePhase(delegatedTransaction); } if (hasDelegatedTransaction) { this.DelegatedTransaction = delegatedTransaction; } } catch (SqlException e) { // we do not want to eat the error if it is a fatal one if (e.Class >= TdsEnums.FATAL_ERROR_CLASS) { throw; } // if the parser is null or its state is not openloggedin, the connection is no longer good. SqlInternalConnectionTds tdsConnection = this as SqlInternalConnectionTds; if (tdsConnection != null) { TdsParser parser = tdsConnection.Parser; if (parser == null || parser.State != TdsParserState.OpenLoggedIn) { throw; } } // In this case, SqlDelegatedTransaction.Initialize // failed and we don't necessarily want to reject // things -- there may have been a legitimate reason // for the failure. } if (!hasDelegatedTransaction) { byte[] cookie = null; if (_isGlobalTransaction) { if (SysTxForGlobalTransactions.GetPromotedToken == null) { throw SQL.UnsupportedSysTxForGlobalTransactions(); } cookie = (byte[])SysTxForGlobalTransactions.GetPromotedToken.Invoke(tx, null); } else { if (null == _whereAbouts) { byte[] dtcAddress = GetDTCAddress(); if (null == dtcAddress) { throw SQL.CannotGetDTCAddress(); } _whereAbouts = dtcAddress; } cookie = GetTransactionCookie(tx, _whereAbouts); } // send cookie to server to finish enlistment PropagateTransactionCookie(cookie); _isEnlistedInTransaction = true; } EnlistedTransaction = tx; // Tell the base class about our enlistment // If we're on a Yukon or newer server, and we delegate the // transaction successfully, we will have done a begin transaction, // which produces a transaction id that we should execute all requests // on. The TdsParser or SmiEventSink will store this information as // the current transaction. // // Likewise, propagating a transaction to a Yukon or newer server will // produce a transaction id that The TdsParser or SmiEventSink will // store as the current transaction. // // In either case, when we're working with a Yukon or newer server // we better have a current transaction by now. Debug.Assert(null != CurrentTransaction, "delegated/enlisted transaction with null current transaction?"); }
private void EnlistNonNull(SysTx.Transaction tx) { Debug.Assert(null != tx, "null transaction?"); if (Bid.AdvancedOn) { Bid.Trace("<sc.SqlInternalConnection.EnlistNonNull|ADV> %d#, transaction %d#.\n", base.ObjectID, tx.GetHashCode()); } bool hasDelegatedTransaction = false; if (IsYukonOrNewer) { if (Bid.AdvancedOn) { Bid.Trace("<sc.SqlInternalConnection.EnlistNonNull|ADV> %d#, attempting to delegate\n", base.ObjectID); } // Promotable transactions are only supported on Yukon // servers or newer. SqlDelegatedTransaction delegatedTransaction = new SqlDelegatedTransaction(this, tx); try { // NOTE: System.Transactions claims to resolve all // potential race conditions between multiple delegate // requests of the same transaction to different // connections in their code, such that only one // attempt to delegate will succeed. // NOTE: PromotableSinglePhaseEnlist will eventually // make a round trip to the server; doing this inside // a lock is not the best choice. We presume that you // aren't trying to enlist concurrently on two threads // and leave it at that -- We don't claim any thread // safety with regard to multiple concurrent requests // to enlist the same connection in different // transactions, which is good, because we don't have // it anyway. // PromotableSinglePhaseEnlist may not actually promote // the transaction when it is already delegated (this is // the way they resolve the race condition when two // threads attempt to delegate the same Lightweight // Transaction) In that case, we can safely ignore // our delegated transaction, and proceed to enlist // in the promoted one. if (tx.EnlistPromotableSinglePhase(delegatedTransaction)) { hasDelegatedTransaction = true; this.DelegatedTransaction = delegatedTransaction; if (Bid.AdvancedOn) { long transactionId = SqlInternalTransaction.NullTransactionId; int transactionObjectID = 0; if (null != CurrentTransaction) { transactionId = CurrentTransaction.TransactionId; transactionObjectID = CurrentTransaction.ObjectID; } Bid.Trace("<sc.SqlInternalConnection.EnlistNonNull|ADV> %d#, delegated to transaction %d# with transactionId=0x%I64x\n", base.ObjectID, transactionObjectID, transactionId); } } } catch (SqlException e) { // we do not want to eat the error if it is a fatal one if (e.Class >= TdsEnums.FATAL_ERROR_CLASS) { throw; } // if the parser is null or its state is not openloggedin, the connection is no longer good. SqlInternalConnectionTds tdsConnection = this as SqlInternalConnectionTds; if (tdsConnection != null) { TdsParser parser = tdsConnection.Parser; if (parser == null || parser.State != TdsParserState.OpenLoggedIn) { throw; } } ADP.TraceExceptionWithoutRethrow(e); // In this case, SqlDelegatedTransaction.Initialize // failed and we don't necessarily want to reject // things -- there may have been a legitimate reason // for the failure. } } if (!hasDelegatedTransaction) { if (Bid.AdvancedOn) { Bid.Trace("<sc.SqlInternalConnection.EnlistNonNull|ADV> %d#, delegation not possible, enlisting.\n", base.ObjectID); } byte[] cookie = null; if (null == _whereAbouts) { byte[] dtcAddress = GetDTCAddress(); if (null == dtcAddress) { throw SQL.CannotGetDTCAddress(); } _whereAbouts = dtcAddress; } cookie = GetTransactionCookie(tx, _whereAbouts); // send cookie to server to finish enlistment PropagateTransactionCookie(cookie); _isEnlistedInTransaction = true; if (Bid.AdvancedOn) { long transactionId = SqlInternalTransaction.NullTransactionId; int transactionObjectID = 0; if (null != CurrentTransaction) { transactionId = CurrentTransaction.TransactionId; transactionObjectID = CurrentTransaction.ObjectID; } Bid.Trace("<sc.SqlInternalConnection.EnlistNonNull|ADV> %d#, enlisted with transaction %d# with transactionId=0x%I64x\n", base.ObjectID, transactionObjectID, transactionId); } } EnlistedTransaction = tx; // Tell the base class about our enlistment // If we're on a Yukon or newer server, and we we delegate the // transaction successfully, we will have done a begin transaction, // which produces a transaction id that we should execute all requests // on. The TdsParser or SmiEventSink will store this information as // the current transaction. // // Likewise, propagating a transaction to a Yukon or newer server will // produce a transaction id that The TdsParser or SmiEventSink will // store as the current transaction. // // In either case, when we're working with a Yukon or newer server // we better have a current transaction by now. Debug.Assert(!IsYukonOrNewer || null != CurrentTransaction, "delegated/enlisted transaction with null current transaction?"); }
private void EnlistNonNull(Transaction tx) { if (Bid.AdvancedOn) { Bid.Trace("<sc.SqlInternalConnection.EnlistNonNull|ADV> %d#, transaction %d#.\n", base.ObjectID, tx.GetHashCode()); } bool flag = false; if (this.IsYukonOrNewer) { if (Bid.AdvancedOn) { Bid.Trace("<sc.SqlInternalConnection.EnlistNonNull|ADV> %d#, attempting to delegate\n", base.ObjectID); } SqlDelegatedTransaction promotableSinglePhaseNotification = new SqlDelegatedTransaction(this, tx); try { if (tx.EnlistPromotableSinglePhase(promotableSinglePhaseNotification)) { flag = true; this.DelegatedTransaction = promotableSinglePhaseNotification; if (Bid.AdvancedOn) { long transactionId = 0L; int objectID = 0; if (this.CurrentTransaction != null) { transactionId = this.CurrentTransaction.TransactionId; objectID = this.CurrentTransaction.ObjectID; } Bid.Trace("<sc.SqlInternalConnection.EnlistNonNull|ADV> %d#, delegated to transaction %d# with transactionId=0x%I64x\n", base.ObjectID, objectID, transactionId); } } } catch (SqlException exception) { if (exception.Class >= 20) { throw; } SqlInternalConnectionTds tds = this as SqlInternalConnectionTds; if (tds != null) { TdsParser parser = tds.Parser; if ((parser == null) || (parser.State != TdsParserState.OpenLoggedIn)) { throw; } } ADP.TraceExceptionWithoutRethrow(exception); } } if (!flag) { if (Bid.AdvancedOn) { Bid.Trace("<sc.SqlInternalConnection.EnlistNonNull|ADV> %d#, delegation not possible, enlisting.\n", base.ObjectID); } byte[] transactionCookie = null; if (this._whereAbouts == null) { byte[] dTCAddress = this.GetDTCAddress(); if (dTCAddress == null) { throw SQL.CannotGetDTCAddress(); } this._whereAbouts = dTCAddress; } transactionCookie = GetTransactionCookie(tx, this._whereAbouts); this.PropagateTransactionCookie(transactionCookie); this._isEnlistedInTransaction = true; if (Bid.AdvancedOn) { long num2 = 0L; int num = 0; if (this.CurrentTransaction != null) { num2 = this.CurrentTransaction.TransactionId; num = this.CurrentTransaction.ObjectID; } Bid.Trace("<sc.SqlInternalConnection.EnlistNonNull|ADV> %d#, enlisted with transaction %d# with transactionId=0x%I64x\n", base.ObjectID, num, num2); } } base.EnlistedTransaction = tx; }