SqlInternalConnection.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. //------------------------------------------------------------------------------
  2. // <copyright file="SqlInternalConnection.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">[....]</owner>
  6. // <owner current="true" primary="false">[....]</owner>
  7. //------------------------------------------------------------------------------
  8. namespace System.Data.SqlClient
  9. {
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Data;
  13. using System.Data.Common;
  14. using System.Data.ProviderBase;
  15. using System.Diagnostics;
  16. using System.Globalization;
  17. using System.Reflection;
  18. using System.Runtime.CompilerServices;
  19. using System.Runtime.ConstrainedExecution;
  20. using System.Runtime.InteropServices;
  21. using System.Security;
  22. using System.Security.Permissions;
  23. using System.Text;
  24. using System.Threading;
  25. using SysTx = System.Transactions;
  26. abstract internal class SqlInternalConnection : DbConnectionInternal {
  27. private readonly SqlConnectionString _connectionOptions;
  28. private bool _isEnlistedInTransaction; // is the server-side connection enlisted? true while we're enlisted, reset only after we send a null...
  29. private byte[] _promotedDTCToken; // token returned by the server when we promote transaction
  30. private byte[] _whereAbouts; // cache the whereabouts (DTC Address) for exporting
  31. // if connection is not open: null
  32. // if connection is open: currently active database
  33. internal string CurrentDatabase { get; set; }
  34. // if connection is not open yet, CurrentDataSource is null
  35. // if connection is open:
  36. // * for regular connections, it is set to Data Source value from connection string
  37. // * for connections with FailoverPartner, it is set to the FailoverPartner value from connection string if the connection was opened to it.
  38. internal string CurrentDataSource { get; set; }
  39. // the delegated (or promoted) transaction we're responsible for.
  40. internal SqlDelegatedTransaction DelegatedTransaction { get; set; }
  41. internal enum TransactionRequest {
  42. Begin,
  43. Promote,
  44. Commit,
  45. Rollback,
  46. IfRollback,
  47. Save
  48. };
  49. internal SqlInternalConnection(SqlConnectionString connectionOptions) : base() {
  50. Debug.Assert(null != connectionOptions, "null connectionOptions?");
  51. _connectionOptions = connectionOptions;
  52. }
  53. internal SqlConnection Connection {
  54. get {
  55. return (SqlConnection)Owner;
  56. }
  57. }
  58. internal SqlConnectionString ConnectionOptions {
  59. get {
  60. return _connectionOptions;
  61. }
  62. }
  63. abstract internal SqlInternalTransaction CurrentTransaction {
  64. get;
  65. }
  66. // SQLBU 415870
  67. // Get the internal transaction that should be hooked to a new outer transaction
  68. // during a BeginTransaction API call. In some cases (i.e. connection is going to
  69. // be reset), CurrentTransaction should not be hooked up this way.
  70. virtual internal SqlInternalTransaction AvailableInternalTransaction {
  71. get {
  72. return CurrentTransaction;
  73. }
  74. }
  75. abstract internal SqlInternalTransaction PendingTransaction {
  76. get;
  77. }
  78. override protected internal bool IsNonPoolableTransactionRoot {
  79. get {
  80. return IsTransactionRoot; // default behavior is that root transactions are NOT poolable. Subclasses may override.
  81. }
  82. }
  83. override internal bool IsTransactionRoot {
  84. get {
  85. var delegatedTransaction = DelegatedTransaction;
  86. return ((null != delegatedTransaction) && (delegatedTransaction.IsActive));
  87. }
  88. }
  89. internal bool HasLocalTransaction {
  90. get {
  91. SqlInternalTransaction currentTransaction = CurrentTransaction;
  92. bool result = (null != currentTransaction && currentTransaction.IsLocal);
  93. return result;
  94. }
  95. }
  96. internal bool HasLocalTransactionFromAPI {
  97. get {
  98. SqlInternalTransaction currentTransaction = CurrentTransaction;
  99. bool result = (null != currentTransaction && currentTransaction.HasParentTransaction);
  100. return result;
  101. }
  102. }
  103. internal bool IsEnlistedInTransaction {
  104. get {
  105. return _isEnlistedInTransaction;
  106. }
  107. }
  108. abstract internal bool IsLockedForBulkCopy {
  109. get;
  110. }
  111. abstract internal bool IsShiloh {
  112. get;
  113. }
  114. abstract internal bool IsYukonOrNewer {
  115. get;
  116. }
  117. abstract internal bool IsKatmaiOrNewer {
  118. get;
  119. }
  120. internal byte[] PromotedDTCToken {
  121. get {
  122. return _promotedDTCToken;
  123. }
  124. set {
  125. _promotedDTCToken = value;
  126. }
  127. }
  128. override public DbTransaction BeginTransaction(IsolationLevel iso) {
  129. return BeginSqlTransaction(iso, null, false);
  130. }
  131. virtual internal SqlTransaction BeginSqlTransaction(IsolationLevel iso, string transactionName, bool shouldReconnect) {
  132. SqlStatistics statistics = null;
  133. TdsParser bestEffortCleanupTarget = null;
  134. RuntimeHelpers.PrepareConstrainedRegions();
  135. try {
  136. #if DEBUG
  137. TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
  138. RuntimeHelpers.PrepareConstrainedRegions();
  139. try {
  140. tdsReliabilitySection.Start();
  141. #else
  142. {
  143. #endif //DEBUG
  144. bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(Connection);
  145. statistics = SqlStatistics.StartTimer(Connection.Statistics);
  146. SqlConnection.ExecutePermission.Demand(); // MDAC 81476
  147. ValidateConnectionForExecute(null);
  148. if (HasLocalTransactionFromAPI)
  149. throw ADP.ParallelTransactionsNotSupported(Connection);
  150. if (iso == IsolationLevel.Unspecified) {
  151. iso = IsolationLevel.ReadCommitted; // Default to ReadCommitted if unspecified.
  152. }
  153. SqlTransaction transaction = new SqlTransaction(this, Connection, iso, AvailableInternalTransaction);
  154. transaction.InternalTransaction.RestoreBrokenConnection = shouldReconnect;
  155. ExecuteTransaction(TransactionRequest.Begin, transactionName, iso, transaction.InternalTransaction, false);
  156. transaction.InternalTransaction.RestoreBrokenConnection = false;
  157. return transaction;
  158. }
  159. #if DEBUG
  160. finally {
  161. tdsReliabilitySection.Stop();
  162. }
  163. #endif //DEBUG
  164. }
  165. catch (System.OutOfMemoryException e) {
  166. Connection.Abort(e);
  167. throw;
  168. }
  169. catch (System.StackOverflowException e) {
  170. Connection.Abort(e);
  171. throw;
  172. }
  173. catch (System.Threading.ThreadAbortException e) {
  174. Connection.Abort(e);
  175. SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
  176. throw;
  177. }
  178. finally {
  179. SqlStatistics.StopTimer(statistics);
  180. }
  181. }
  182. override public void ChangeDatabase(string database) {
  183. SqlConnection.ExecutePermission.Demand(); // MDAC 80961
  184. if (ADP.IsEmpty(database)) {
  185. throw ADP.EmptyDatabaseName();
  186. }
  187. ValidateConnectionForExecute(null); //
  188. ChangeDatabaseInternal(database); // do the real work...
  189. }
  190. abstract protected void ChangeDatabaseInternal(string database);
  191. override protected void CleanupTransactionOnCompletion(SysTx.Transaction transaction) {
  192. // Note: unlocked, potentially multi-threaded code, so pull delegate to local to
  193. // ensure it doesn't change between test and call.
  194. SqlDelegatedTransaction delegatedTransaction = DelegatedTransaction;
  195. if (null != delegatedTransaction) {
  196. delegatedTransaction.TransactionEnded(transaction);
  197. }
  198. }
  199. override protected DbReferenceCollection CreateReferenceCollection() {
  200. return new SqlReferenceCollection();
  201. }
  202. override protected void Deactivate() {
  203. if (Bid.AdvancedOn) {
  204. Bid.Trace("<sc.SqlInternalConnection.Deactivate|ADV> %d# deactivating\n", base.ObjectID);
  205. }
  206. TdsParser bestEffortCleanupTarget = null;
  207. RuntimeHelpers.PrepareConstrainedRegions();
  208. try {
  209. #if DEBUG
  210. TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
  211. RuntimeHelpers.PrepareConstrainedRegions();
  212. try {
  213. tdsReliabilitySection.Start();
  214. #else
  215. {
  216. #endif //DEBUG
  217. bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(Connection);
  218. SqlReferenceCollection referenceCollection = (SqlReferenceCollection)ReferenceCollection;
  219. if (null != referenceCollection) {
  220. referenceCollection.Deactivate();
  221. }
  222. // Invoke subclass-specific deactivation logic
  223. InternalDeactivate();
  224. }
  225. #if DEBUG
  226. finally {
  227. tdsReliabilitySection.Stop();
  228. }
  229. #endif //DEBUG
  230. }
  231. catch (System.OutOfMemoryException) {
  232. DoomThisConnection();
  233. throw;
  234. }
  235. catch (System.StackOverflowException) {
  236. DoomThisConnection();
  237. throw;
  238. }
  239. catch (System.Threading.ThreadAbortException) {
  240. DoomThisConnection();
  241. SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
  242. throw;
  243. }
  244. catch (Exception e) {
  245. //
  246. if (!ADP.IsCatchableExceptionType(e)) {
  247. throw;
  248. }
  249. // if an exception occurred, the inner connection will be
  250. // marked as unusable and destroyed upon returning to the
  251. // pool
  252. DoomThisConnection();
  253. ADP.TraceExceptionWithoutRethrow(e);
  254. }
  255. }
  256. abstract internal void DisconnectTransaction(SqlInternalTransaction internalTransaction);
  257. override public void Dispose() {
  258. _whereAbouts = null;
  259. base.Dispose();
  260. }
  261. protected void Enlist(SysTx.Transaction tx) {
  262. // This method should not be called while the connection has a
  263. // reference to an active delegated transaction.
  264. // Manual enlistment via SqlConnection.EnlistTransaction
  265. // should catch this case and throw an exception.
  266. //
  267. // Automatic enlistment isn't possible because
  268. // Sys.Tx keeps the connection alive until the transaction is completed.
  269. Debug.Assert (!IsNonPoolableTransactionRoot, "cannot defect an active delegated transaction!"); // potential race condition, but it's an assert
  270. if (null == tx) {
  271. if (IsEnlistedInTransaction)
  272. {
  273. EnlistNull();
  274. }
  275. else
  276. {
  277. // When IsEnlistedInTransaction is false, it means we are in one of two states:
  278. // 1. EnlistTransaction is null, so the connection is truly not enlisted in a transaction, or
  279. // 2. Connection is enlisted in a SqlDelegatedTransaction.
  280. //
  281. // For #2, we have to consider whether or not the delegated transaction is active.
  282. // If it is not active, we allow the enlistment in the NULL transaction.
  283. //
  284. // If it is active, technically this is an error.
  285. // However, no exception is thrown as this was the precedent (and this case is silently ignored, no error, but no enlistment either).
  286. // There are two mitigations for this:
  287. // 1. SqlConnection.EnlistTransaction checks that the enlisted transaction has completed before allowing a different enlistment.
  288. // 2. For debug builds, the assert at the beginning of this method checks for an enlistment in an active delegated transaction.
  289. SysTx.Transaction enlistedTransaction = EnlistedTransaction;
  290. if (enlistedTransaction != null && enlistedTransaction.TransactionInformation.Status != SysTx.TransactionStatus.Active)
  291. {
  292. EnlistNull();
  293. }
  294. }
  295. }
  296. // Only enlist if it's different...
  297. else if (!tx.Equals(EnlistedTransaction)) { // WebData 20000024 - Must use Equals, not !=
  298. EnlistNonNull(tx);
  299. }
  300. }
  301. private void EnlistNonNull(SysTx.Transaction tx) {
  302. Debug.Assert(null != tx, "null transaction?");
  303. if (Bid.AdvancedOn) {
  304. Bid.Trace("<sc.SqlInternalConnection.EnlistNonNull|ADV> %d#, transaction %d#.\n", base.ObjectID, tx.GetHashCode());
  305. }
  306. bool hasDelegatedTransaction = false;
  307. if (IsYukonOrNewer) {
  308. if (Bid.AdvancedOn) {
  309. Bid.Trace("<sc.SqlInternalConnection.EnlistNonNull|ADV> %d#, attempting to delegate\n", base.ObjectID);
  310. }
  311. // Promotable transactions are only supported on Yukon
  312. // servers or newer.
  313. SqlDelegatedTransaction delegatedTransaction = new SqlDelegatedTransaction(this, tx);
  314. try {
  315. // NOTE: System.Transactions claims to resolve all
  316. // potential race conditions between multiple delegate
  317. // requests of the same transaction to different
  318. // connections in their code, such that only one
  319. // attempt to delegate will succeed.
  320. // NOTE: PromotableSinglePhaseEnlist will eventually
  321. // make a round trip to the server; doing this inside
  322. // a lock is not the best choice. We presume that you
  323. // aren't trying to enlist concurrently on two threads
  324. // and leave it at that -- We don't claim any thread
  325. // safety with regard to multiple concurrent requests
  326. // to enlist the same connection in different
  327. // transactions, which is good, because we don't have
  328. // it anyway.
  329. // PromotableSinglePhaseEnlist may not actually promote
  330. // the transaction when it is already delegated (this is
  331. // the way they resolve the race condition when two
  332. // threads attempt to delegate the same Lightweight
  333. // Transaction) In that case, we can safely ignore
  334. // our delegated transaction, and proceed to enlist
  335. // in the promoted one.
  336. if (tx.EnlistPromotableSinglePhase(delegatedTransaction)) {
  337. hasDelegatedTransaction = true;
  338. this.DelegatedTransaction = delegatedTransaction;
  339. if (Bid.AdvancedOn) {
  340. long transactionId = SqlInternalTransaction.NullTransactionId;
  341. int transactionObjectID = 0;
  342. if (null != CurrentTransaction) {
  343. transactionId = CurrentTransaction.TransactionId;
  344. transactionObjectID = CurrentTransaction.ObjectID;
  345. }
  346. Bid.Trace("<sc.SqlInternalConnection.EnlistNonNull|ADV> %d#, delegated to transaction %d# with transactionId=0x%I64x\n", base.ObjectID, transactionObjectID, transactionId);
  347. }
  348. }
  349. }
  350. catch (SqlException e) {
  351. // we do not want to eat the error if it is a fatal one
  352. if (e.Class >= TdsEnums.FATAL_ERROR_CLASS) {
  353. throw;
  354. }
  355. // if the parser is null or its state is not openloggedin, the connection is no longer good.
  356. SqlInternalConnectionTds tdsConnection = this as SqlInternalConnectionTds;
  357. if (tdsConnection != null)
  358. {
  359. TdsParser parser = tdsConnection.Parser;
  360. if (parser == null || parser.State != TdsParserState.OpenLoggedIn)
  361. {
  362. throw;
  363. }
  364. }
  365. ADP.TraceExceptionWithoutRethrow(e);
  366. // In this case, SqlDelegatedTransaction.Initialize
  367. // failed and we don't necessarily want to reject
  368. // things -- there may have been a legitimate reason
  369. // for the failure.
  370. }
  371. }
  372. if (!hasDelegatedTransaction) {
  373. if (Bid.AdvancedOn) {
  374. Bid.Trace("<sc.SqlInternalConnection.EnlistNonNull|ADV> %d#, delegation not possible, enlisting.\n", base.ObjectID);
  375. }
  376. byte[] cookie = null;
  377. if (null == _whereAbouts) {
  378. byte[] dtcAddress = GetDTCAddress();
  379. if (null == dtcAddress) {
  380. throw SQL.CannotGetDTCAddress();
  381. }
  382. _whereAbouts = dtcAddress;
  383. }
  384. cookie = GetTransactionCookie(tx, _whereAbouts);
  385. // send cookie to server to finish enlistment
  386. PropagateTransactionCookie(cookie);
  387. _isEnlistedInTransaction = true;
  388. if (Bid.AdvancedOn) {
  389. long transactionId = SqlInternalTransaction.NullTransactionId;
  390. int transactionObjectID = 0;
  391. if (null != CurrentTransaction) {
  392. transactionId = CurrentTransaction.TransactionId;
  393. transactionObjectID = CurrentTransaction.ObjectID;
  394. }
  395. Bid.Trace("<sc.SqlInternalConnection.EnlistNonNull|ADV> %d#, enlisted with transaction %d# with transactionId=0x%I64x\n", base.ObjectID, transactionObjectID, transactionId);
  396. }
  397. }
  398. EnlistedTransaction = tx; // Tell the base class about our enlistment
  399. // If we're on a Yukon or newer server, and we we delegate the
  400. // transaction successfully, we will have done a begin transaction,
  401. // which produces a transaction id that we should execute all requests
  402. // on. The TdsParser or SmiEventSink will store this information as
  403. // the current transaction.
  404. //
  405. // Likewise, propagating a transaction to a Yukon or newer server will
  406. // produce a transaction id that The TdsParser or SmiEventSink will
  407. // store as the current transaction.
  408. //
  409. // In either case, when we're working with a Yukon or newer server
  410. // we better have a current transaction by now.
  411. Debug.Assert(!IsYukonOrNewer || null != CurrentTransaction, "delegated/enlisted transaction with null current transaction?");
  412. }
  413. internal void EnlistNull() {
  414. if (Bid.AdvancedOn) {
  415. Bid.Trace("<sc.SqlInternalConnection.EnlistNull|ADV> %d#, unenlisting.\n", base.ObjectID);
  416. }
  417. // We were in a transaction, but now we are not - so send
  418. // message to server with empty transaction - confirmed proper
  419. // behavior from Sameet Agarwal
  420. //
  421. // The connection pooler maintains separate pools for enlisted
  422. // transactions, and only when that transaction is committed or
  423. // rolled back will those connections be taken from that
  424. // separate pool and returned to the general pool of connections
  425. // that are not affiliated with any transactions. When this
  426. // occurs, we will have a new transaction of null and we are
  427. // required to send an empty transaction payload to the server.
  428. PropagateTransactionCookie(null);
  429. _isEnlistedInTransaction = false;
  430. EnlistedTransaction = null; // Tell the base class about our enlistment
  431. if (Bid.AdvancedOn) {
  432. Bid.Trace("<sc.SqlInternalConnection.EnlistNull|ADV> %d#, unenlisted.\n", base.ObjectID);
  433. }
  434. // The EnlistTransaction above will return an TransactionEnded event,
  435. // which causes the TdsParser or SmiEventSink should to clear the
  436. // current transaction.
  437. //
  438. // In either case, when we're working with a Yukon or newer server
  439. // we better not have a current transaction at this point.
  440. Debug.Assert(!IsYukonOrNewer || null == CurrentTransaction, "unenlisted transaction with non-null current transaction?"); // verify it!
  441. }
  442. override public void EnlistTransaction(SysTx.Transaction transaction) {
  443. SqlConnection.VerifyExecutePermission();
  444. ValidateConnectionForExecute(null);
  445. // If a connection has a local transaction outstanding and you try
  446. // to enlist in a DTC transaction, SQL Server will rollback the
  447. // local transaction and then do the enlist (7.0 and 2000). So, if
  448. // the user tries to do this, throw.
  449. if (HasLocalTransaction) {
  450. throw ADP.LocalTransactionPresent();
  451. }
  452. if (null != transaction && transaction.Equals(EnlistedTransaction)) {
  453. // No-op if this is the current transaction
  454. return;
  455. }
  456. // If a connection is already enlisted in a DTC transaction and you
  457. // try to enlist in another one, in 7.0 the existing DTC transaction
  458. // would roll back and then the connection would enlist in the new
  459. // one. In SQL 2000 & Yukon, when you enlist in a DTC transaction
  460. // while the connection is already enlisted in a DTC transaction,
  461. // the connection simply switches enlistments. Regardless, simply
  462. // enlist in the user specified distributed transaction. This
  463. // behavior matches OLEDB and ODBC.
  464. TdsParser bestEffortCleanupTarget = null;
  465. RuntimeHelpers.PrepareConstrainedRegions();
  466. try {
  467. #if DEBUG
  468. TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
  469. RuntimeHelpers.PrepareConstrainedRegions();
  470. try {
  471. tdsReliabilitySection.Start();
  472. #else
  473. {
  474. #endif //DEBUG
  475. bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(Connection);
  476. Enlist(transaction);
  477. }
  478. #if DEBUG
  479. finally {
  480. tdsReliabilitySection.Stop();
  481. }
  482. #endif //DEBUG
  483. }
  484. catch (System.OutOfMemoryException e) {
  485. Connection.Abort(e);
  486. throw;
  487. }
  488. catch (System.StackOverflowException e) {
  489. Connection.Abort(e);
  490. throw;
  491. }
  492. catch (System.Threading.ThreadAbortException e) {
  493. Connection.Abort(e);
  494. SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
  495. throw;
  496. }
  497. }
  498. abstract internal void ExecuteTransaction(TransactionRequest transactionRequest, string name, IsolationLevel iso, SqlInternalTransaction internalTransaction, bool isDelegateControlRequest);
  499. internal SqlDataReader FindLiveReader(SqlCommand command) {
  500. SqlDataReader reader = null;
  501. SqlReferenceCollection referenceCollection = (SqlReferenceCollection)ReferenceCollection;
  502. if (null != referenceCollection) {
  503. reader = referenceCollection.FindLiveReader(command);
  504. }
  505. return reader;
  506. }
  507. internal SqlCommand FindLiveCommand(TdsParserStateObject stateObj) {
  508. SqlCommand command = null;
  509. SqlReferenceCollection referenceCollection = (SqlReferenceCollection)ReferenceCollection;
  510. if (null != referenceCollection) {
  511. command = referenceCollection.FindLiveCommand(stateObj);
  512. }
  513. return command;
  514. }
  515. static internal TdsParser GetBestEffortCleanupTarget(SqlConnection connection) {
  516. if (null != connection) {
  517. SqlInternalConnectionTds innerConnection = (connection.InnerConnection as SqlInternalConnectionTds);
  518. if (null != innerConnection) {
  519. return innerConnection.Parser;
  520. }
  521. }
  522. return null;
  523. }
  524. [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  525. static internal void BestEffortCleanup(TdsParser target) {
  526. if (null != target) {
  527. target.BestEffortCleanup();
  528. }
  529. }
  530. abstract protected byte[] GetDTCAddress();
  531. static private byte[] GetTransactionCookie(SysTx.Transaction transaction, byte[] whereAbouts) {
  532. byte[] transactionCookie = null;
  533. if (null != transaction) {
  534. transactionCookie = SysTx.TransactionInterop.GetExportCookie(transaction, whereAbouts);
  535. }
  536. return transactionCookie;
  537. }
  538. virtual protected void InternalDeactivate() {
  539. }
  540. // If wrapCloseInAction is defined, then the action it defines will be run with the connection close action passed in as a parameter
  541. // The close action also supports being run asynchronously
  542. internal void OnError(SqlException exception, bool breakConnection, Action<Action> wrapCloseInAction = null) {
  543. if (breakConnection) {
  544. DoomThisConnection();
  545. }
  546. var connection = Connection;
  547. if (null != connection) {
  548. connection.OnError(exception, breakConnection, wrapCloseInAction);
  549. }
  550. else if (exception.Class >= TdsEnums.MIN_ERROR_CLASS) {
  551. // It is an error, and should be thrown. Class of TdsEnums.MIN_ERROR_CLASS
  552. // or above is an error, below TdsEnums.MIN_ERROR_CLASS denotes an info message.
  553. throw exception;
  554. }
  555. }
  556. abstract protected void PropagateTransactionCookie(byte[] transactionCookie);
  557. abstract internal void ValidateConnectionForExecute(SqlCommand command);
  558. }
  559. }