| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717 |
- //------------------------------------------------------------------------------
- // <copyright file="SqlInternalConnection.cs" company="Microsoft">
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // </copyright>
- // <owner current="true" primary="true">[....]</owner>
- // <owner current="true" primary="false">[....]</owner>
- //------------------------------------------------------------------------------
- namespace System.Data.SqlClient
- {
- using System;
- using System.Collections.Generic;
- using System.Data;
- using System.Data.Common;
- using System.Data.ProviderBase;
- using System.Diagnostics;
- using System.Globalization;
- using System.Reflection;
- using System.Runtime.CompilerServices;
- using System.Runtime.ConstrainedExecution;
- using System.Runtime.InteropServices;
- using System.Security;
- using System.Security.Permissions;
- using System.Text;
- using System.Threading;
- using SysTx = System.Transactions;
- abstract internal class SqlInternalConnection : DbConnectionInternal {
- private readonly SqlConnectionString _connectionOptions;
- private bool _isEnlistedInTransaction; // is the server-side connection enlisted? true while we're enlisted, reset only after we send a null...
- private byte[] _promotedDTCToken; // token returned by the server when we promote transaction
- private byte[] _whereAbouts; // cache the whereabouts (DTC Address) for exporting
-
- private bool _isGlobalTransaction = false; // Whether this is a Global Transaction (Non-MSDTC, Azure SQL DB Transaction)
- private bool _isGlobalTransactionEnabledForServer = false; // Whether Global Transactions are enabled for this Azure SQL DB Server
- private static readonly Guid _globalTransactionTMID = new Guid("1c742caf-6680-40ea-9c26-6b6846079764"); // ID of the Non-MSDTC, Azure SQL DB Transaction Manager
-
- // if connection is not open: null
- // if connection is open: currently active database
- internal string CurrentDatabase { get; set; }
- // if connection is not open yet, CurrentDataSource is null
- // if connection is open:
- // * for regular connections, it is set to Data Source value from connection string
- // * for connections with FailoverPartner, it is set to the FailoverPartner value from connection string if the connection was opened to it.
- internal string CurrentDataSource { get; set; }
- // the delegated (or promoted) transaction we're responsible for.
- internal SqlDelegatedTransaction DelegatedTransaction { get; set; }
- internal enum TransactionRequest {
- Begin,
- Promote,
- Commit,
- Rollback,
- IfRollback,
- Save
- };
- internal SqlInternalConnection(SqlConnectionString connectionOptions) : base() {
- Debug.Assert(null != connectionOptions, "null connectionOptions?");
- _connectionOptions = connectionOptions;
- }
- internal SqlConnection Connection {
- get {
- return (SqlConnection)Owner;
- }
- }
- internal SqlConnectionString ConnectionOptions {
- get {
- return _connectionOptions;
- }
- }
- abstract internal SqlInternalTransaction CurrentTransaction {
- get;
- }
- // SQLBU 415870
- // Get the internal transaction that should be hooked to a new outer transaction
- // during a BeginTransaction API call. In some cases (i.e. connection is going to
- // be reset), CurrentTransaction should not be hooked up this way.
- virtual internal SqlInternalTransaction AvailableInternalTransaction {
- get {
- return CurrentTransaction;
- }
- }
- abstract internal SqlInternalTransaction PendingTransaction {
- get;
- }
- override protected internal bool IsNonPoolableTransactionRoot {
- get {
- return IsTransactionRoot; // default behavior is that root transactions are NOT poolable. Subclasses may override.
- }
- }
- override internal bool IsTransactionRoot {
- get {
- var delegatedTransaction = DelegatedTransaction;
- return ((null != delegatedTransaction) && (delegatedTransaction.IsActive));
- }
- }
- internal bool HasLocalTransaction {
- get {
- SqlInternalTransaction currentTransaction = CurrentTransaction;
- bool result = (null != currentTransaction && currentTransaction.IsLocal);
- return result;
- }
- }
- internal bool HasLocalTransactionFromAPI {
- get {
- SqlInternalTransaction currentTransaction = CurrentTransaction;
- bool result = (null != currentTransaction && currentTransaction.HasParentTransaction);
- return result;
- }
- }
- internal bool IsEnlistedInTransaction {
- get {
- return _isEnlistedInTransaction;
- }
- }
- abstract internal bool IsLockedForBulkCopy {
- get;
- }
- abstract internal bool IsShiloh {
- get;
- }
- abstract internal bool IsYukonOrNewer {
- get;
- }
- abstract internal bool IsKatmaiOrNewer {
- get;
- }
- internal byte[] PromotedDTCToken {
- get {
- return _promotedDTCToken;
- }
- set {
- _promotedDTCToken = value;
- }
- }
- internal bool IsGlobalTransaction {
- get {
- return _isGlobalTransaction;
- }
- set {
- _isGlobalTransaction = value;
- }
- }
- internal bool IsGlobalTransactionsEnabledForServer {
- get {
- return _isGlobalTransactionEnabledForServer;
- }
- set {
- _isGlobalTransactionEnabledForServer = value;
- }
- }
- override public DbTransaction BeginTransaction(IsolationLevel iso) {
- return BeginSqlTransaction(iso, null, false);
- }
- virtual internal SqlTransaction BeginSqlTransaction(IsolationLevel iso, string transactionName, bool shouldReconnect) {
- SqlStatistics statistics = null;
- TdsParser bestEffortCleanupTarget = null;
- RuntimeHelpers.PrepareConstrainedRegions();
- try {
- #if DEBUG
- TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
- RuntimeHelpers.PrepareConstrainedRegions();
- try {
- tdsReliabilitySection.Start();
- #else
- {
- #endif //DEBUG
- bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(Connection);
- statistics = SqlStatistics.StartTimer(Connection.Statistics);
- SqlConnection.ExecutePermission.Demand(); // MDAC 81476
- ValidateConnectionForExecute(null);
- if (HasLocalTransactionFromAPI)
- throw ADP.ParallelTransactionsNotSupported(Connection);
- if (iso == IsolationLevel.Unspecified) {
- iso = IsolationLevel.ReadCommitted; // Default to ReadCommitted if unspecified.
- }
- SqlTransaction transaction = new SqlTransaction(this, Connection, iso, AvailableInternalTransaction);
- transaction.InternalTransaction.RestoreBrokenConnection = shouldReconnect;
- ExecuteTransaction(TransactionRequest.Begin, transactionName, iso, transaction.InternalTransaction, false);
- transaction.InternalTransaction.RestoreBrokenConnection = false;
- return transaction;
- }
- #if DEBUG
- finally {
- tdsReliabilitySection.Stop();
- }
- #endif //DEBUG
- }
- catch (System.OutOfMemoryException e) {
- Connection.Abort(e);
- throw;
- }
- catch (System.StackOverflowException e) {
- Connection.Abort(e);
- throw;
- }
- catch (System.Threading.ThreadAbortException e) {
- Connection.Abort(e);
- SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
- throw;
- }
- finally {
- SqlStatistics.StopTimer(statistics);
- }
- }
- override public void ChangeDatabase(string database) {
- SqlConnection.ExecutePermission.Demand(); // MDAC 80961
- if (ADP.IsEmpty(database)) {
- throw ADP.EmptyDatabaseName();
- }
- ValidateConnectionForExecute(null); //
- ChangeDatabaseInternal(database); // do the real work...
- }
- abstract protected void ChangeDatabaseInternal(string database);
-
- override protected void CleanupTransactionOnCompletion(SysTx.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);
- }
- }
- override protected DbReferenceCollection CreateReferenceCollection() {
- return new SqlReferenceCollection();
- }
- override protected void Deactivate() {
- if (Bid.AdvancedOn) {
- Bid.Trace("<sc.SqlInternalConnection.Deactivate|ADV> %d# deactivating\n", base.ObjectID);
- }
- TdsParser bestEffortCleanupTarget = null;
- RuntimeHelpers.PrepareConstrainedRegions();
- try {
- #if DEBUG
- TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
- RuntimeHelpers.PrepareConstrainedRegions();
- try {
- tdsReliabilitySection.Start();
- #else
- {
- #endif //DEBUG
- bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(Connection);
- SqlReferenceCollection referenceCollection = (SqlReferenceCollection)ReferenceCollection;
- if (null != referenceCollection) {
- referenceCollection.Deactivate();
- }
- // Invoke subclass-specific deactivation logic
- InternalDeactivate();
- }
- #if DEBUG
- finally {
- tdsReliabilitySection.Stop();
- }
- #endif //DEBUG
- }
- catch (System.OutOfMemoryException) {
- DoomThisConnection();
- throw;
- }
- catch (System.StackOverflowException) {
- DoomThisConnection();
- throw;
- }
- catch (System.Threading.ThreadAbortException) {
- DoomThisConnection();
- SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
- throw;
- }
- catch (Exception e) {
- //
- if (!ADP.IsCatchableExceptionType(e)) {
- throw;
- }
- // if an exception occurred, the inner connection will be
- // marked as unusable and destroyed upon returning to the
- // pool
- DoomThisConnection();
- ADP.TraceExceptionWithoutRethrow(e);
- }
- }
-
- abstract internal void DisconnectTransaction(SqlInternalTransaction internalTransaction);
- override public void Dispose() {
- _whereAbouts = null;
- base.Dispose();
- }
- protected void Enlist(SysTx.Transaction tx) {
- // This method should not be called while the connection has a
- // reference to an active delegated transaction.
- // Manual enlistment via SqlConnection.EnlistTransaction
- // should catch this case and throw an exception.
- //
- // Automatic enlistment isn't possible because
- // Sys.Tx keeps the connection alive until the transaction is completed.
- Debug.Assert (!IsNonPoolableTransactionRoot, "cannot defect an active delegated transaction!"); // potential race condition, but it's an assert
- if (null == tx) {
- if (IsEnlistedInTransaction)
- {
- EnlistNull();
- }
- else
- {
- // When IsEnlistedInTransaction is false, it means we are in one of two states:
- // 1. EnlistTransaction is null, so the connection is truly not enlisted in a transaction, or
- // 2. Connection is enlisted in a SqlDelegatedTransaction.
- //
- // For #2, we have to consider whether or not the delegated transaction is active.
- // If it is not active, we allow the enlistment in the NULL transaction.
- //
- // If it is active, technically this is an error.
- // However, no exception is thrown as this was the precedent (and this case is silently ignored, no error, but no enlistment either).
- // There are two mitigations for this:
- // 1. SqlConnection.EnlistTransaction checks that the enlisted transaction has completed before allowing a different enlistment.
- // 2. For debug builds, the assert at the beginning of this method checks for an enlistment in an active delegated transaction.
- SysTx.Transaction enlistedTransaction = EnlistedTransaction;
- if (enlistedTransaction != null && enlistedTransaction.TransactionInformation.Status != SysTx.TransactionStatus.Active)
- {
- EnlistNull();
- }
- }
- }
- // Only enlist if it's different...
- else if (!tx.Equals(EnlistedTransaction)) { // WebData 20000024 - Must use Equals, not !=
- EnlistNonNull(tx);
- }
- }
- 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.
- // 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;
- 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 (_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;
- 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?");
- }
- internal void EnlistNull() {
- if (Bid.AdvancedOn) {
- Bid.Trace("<sc.SqlInternalConnection.EnlistNull|ADV> %d#, unenlisting.\n", base.ObjectID);
- }
- // We were in a transaction, but now we are not - so send
- // message to server with empty transaction - confirmed proper
- // behavior from Sameet Agarwal
- //
- // The connection pooler maintains separate pools for enlisted
- // transactions, and only when that transaction is committed or
- // rolled back will those connections be taken from that
- // separate pool and returned to the general pool of connections
- // that are not affiliated with any transactions. When this
- // occurs, we will have a new transaction of null and we are
- // required to send an empty transaction payload to the server.
- PropagateTransactionCookie(null);
- _isEnlistedInTransaction = false;
- EnlistedTransaction = null; // Tell the base class about our enlistment
- if (Bid.AdvancedOn) {
- Bid.Trace("<sc.SqlInternalConnection.EnlistNull|ADV> %d#, unenlisted.\n", base.ObjectID);
- }
- // The EnlistTransaction above will return an TransactionEnded event,
- // which causes the TdsParser or SmiEventSink should to clear the
- // current transaction.
- //
- // In either case, when we're working with a Yukon or newer server
- // we better not have a current transaction at this point.
- Debug.Assert(!IsYukonOrNewer || null == CurrentTransaction, "unenlisted transaction with non-null current transaction?"); // verify it!
- }
-
- override public void EnlistTransaction(SysTx.Transaction transaction) {
- SqlConnection.VerifyExecutePermission();
- ValidateConnectionForExecute(null);
- // If a connection has a local transaction outstanding and you try
- // to enlist in a DTC transaction, SQL Server will rollback the
- // local transaction and then do the enlist (7.0 and 2000). So, if
- // the user tries to do this, throw.
- if (HasLocalTransaction) {
- throw ADP.LocalTransactionPresent();
- }
- if (null != transaction && transaction.Equals(EnlistedTransaction)) {
- // No-op if this is the current transaction
- return;
- }
- // If a connection is already enlisted in a DTC transaction and you
- // try to enlist in another one, in 7.0 the existing DTC transaction
- // would roll back and then the connection would enlist in the new
- // one. In SQL 2000 & Yukon, when you enlist in a DTC transaction
- // while the connection is already enlisted in a DTC transaction,
- // the connection simply switches enlistments. Regardless, simply
- // enlist in the user specified distributed transaction. This
- // behavior matches OLEDB and ODBC.
- TdsParser bestEffortCleanupTarget = null;
- RuntimeHelpers.PrepareConstrainedRegions();
- try {
- #if DEBUG
- TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
- RuntimeHelpers.PrepareConstrainedRegions();
- try {
- tdsReliabilitySection.Start();
- #else
- {
- #endif //DEBUG
- bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(Connection);
- Enlist(transaction);
- }
- #if DEBUG
- finally {
- tdsReliabilitySection.Stop();
- }
- #endif //DEBUG
- }
- catch (System.OutOfMemoryException e) {
- Connection.Abort(e);
- throw;
- }
- catch (System.StackOverflowException e) {
- Connection.Abort(e);
- throw;
- }
- catch (System.Threading.ThreadAbortException e) {
- Connection.Abort(e);
- SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
- throw;
- }
- }
- abstract internal void ExecuteTransaction(TransactionRequest transactionRequest, string name, IsolationLevel iso, SqlInternalTransaction internalTransaction, bool isDelegateControlRequest);
- internal SqlDataReader FindLiveReader(SqlCommand command) {
- SqlDataReader reader = null;
- SqlReferenceCollection referenceCollection = (SqlReferenceCollection)ReferenceCollection;
- if (null != referenceCollection) {
- reader = referenceCollection.FindLiveReader(command);
- }
- return reader;
- }
- internal SqlCommand FindLiveCommand(TdsParserStateObject stateObj) {
- SqlCommand command = null;
- SqlReferenceCollection referenceCollection = (SqlReferenceCollection)ReferenceCollection;
- if (null != referenceCollection) {
- command = referenceCollection.FindLiveCommand(stateObj);
- }
- return command;
- }
- static internal TdsParser GetBestEffortCleanupTarget(SqlConnection connection) {
- if (null != connection) {
- SqlInternalConnectionTds innerConnection = (connection.InnerConnection as SqlInternalConnectionTds);
- if (null != innerConnection) {
- return innerConnection.Parser;
- }
- }
- return null;
- }
- [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
- static internal void BestEffortCleanup(TdsParser target) {
- if (null != target) {
- target.BestEffortCleanup();
- }
- }
- abstract protected byte[] GetDTCAddress();
- static private byte[] GetTransactionCookie(SysTx.Transaction transaction, byte[] whereAbouts) {
- byte[] transactionCookie = null;
- if (null != transaction) {
- transactionCookie = SysTx.TransactionInterop.GetExportCookie(transaction, whereAbouts);
- }
- return transactionCookie;
- }
- virtual protected void InternalDeactivate() {
- }
- // If wrapCloseInAction is defined, then the action it defines will be run with the connection close action passed in as a parameter
- // The close action also supports being run asynchronously
- internal void OnError(SqlException exception, bool breakConnection, Action<Action> wrapCloseInAction = null) {
- if (breakConnection) {
- DoomThisConnection();
- }
- var connection = Connection;
- if (null != connection) {
- connection.OnError(exception, breakConnection, wrapCloseInAction);
- }
- else if (exception.Class >= TdsEnums.MIN_ERROR_CLASS) {
- // It is an error, and should be thrown. Class of TdsEnums.MIN_ERROR_CLASS
- // or above is an error, below TdsEnums.MIN_ERROR_CLASS denotes an info message.
- throw exception;
- }
- }
- abstract protected void PropagateTransactionCookie(byte[] transactionCookie);
-
- abstract internal void ValidateConnectionForExecute(SqlCommand command);
- }
- }
|