SqlTransaction.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. //------------------------------------------------------------------------------
  2. // <copyright file="SqlTransaction.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. using System.Data;
  10. using System.Data.Common;
  11. using System.Data.ProviderBase;
  12. using System.Data.Sql;
  13. using System.Data.SqlTypes;
  14. using System.Diagnostics;
  15. using System.Runtime.CompilerServices;
  16. using System.Runtime.ConstrainedExecution;
  17. using System.Threading;
  18. public sealed class SqlTransaction : DbTransaction {
  19. private static int _objectTypeCount; // Bid counter
  20. internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
  21. internal readonly IsolationLevel _isolationLevel = IsolationLevel.ReadCommitted;
  22. private SqlInternalTransaction _internalTransaction;
  23. private SqlConnection _connection;
  24. private bool _isFromAPI;
  25. internal SqlTransaction(SqlInternalConnection internalConnection, SqlConnection con,
  26. IsolationLevel iso, SqlInternalTransaction internalTransaction) {
  27. SqlConnection.VerifyExecutePermission();
  28. _isolationLevel = iso;
  29. _connection = con;
  30. if (internalTransaction == null) {
  31. _internalTransaction = new SqlInternalTransaction(internalConnection, TransactionType.LocalFromAPI, this);
  32. }
  33. else {
  34. Debug.Assert(internalConnection.CurrentTransaction == internalTransaction, "Unexpected Parser.CurrentTransaction state!");
  35. _internalTransaction = internalTransaction;
  36. _internalTransaction.InitParent(this);
  37. }
  38. }
  39. ////////////////////////////////////////////////////////////////////////////////////////
  40. // PROPERTIES
  41. ////////////////////////////////////////////////////////////////////////////////////////
  42. new public SqlConnection Connection { // MDAC 66655
  43. get {
  44. if (IsZombied) {
  45. return null;
  46. }
  47. else {
  48. return _connection;
  49. }
  50. }
  51. }
  52. override protected DbConnection DbConnection {
  53. get {
  54. return Connection;
  55. }
  56. }
  57. internal SqlInternalTransaction InternalTransaction {
  58. get {
  59. return _internalTransaction;
  60. }
  61. }
  62. override public IsolationLevel IsolationLevel {
  63. get {
  64. ZombieCheck();
  65. return _isolationLevel;
  66. }
  67. }
  68. private bool IsYukonPartialZombie {
  69. get {
  70. return (null != _internalTransaction && _internalTransaction.IsCompleted);
  71. }
  72. }
  73. internal bool IsZombied {
  74. get {
  75. return (null == _internalTransaction || _internalTransaction.IsCompleted);
  76. }
  77. }
  78. internal int ObjectID {
  79. get {
  80. return _objectID;
  81. }
  82. }
  83. internal SqlStatistics Statistics {
  84. get {
  85. if (null != _connection) {
  86. if (_connection.StatisticsEnabled) {
  87. return _connection.Statistics;
  88. }
  89. }
  90. return null;
  91. }
  92. }
  93. ////////////////////////////////////////////////////////////////////////////////////////
  94. // PUBLIC METHODS
  95. ////////////////////////////////////////////////////////////////////////////////////////
  96. override public void Commit() {
  97. SqlConnection.ExecutePermission.Demand(); // MDAC 81476
  98. ZombieCheck();
  99. SqlStatistics statistics = null;
  100. IntPtr hscp;
  101. Bid.ScopeEnter(out hscp, "<sc.SqlTransaction.Commit|API> %d#", ObjectID);
  102. Bid.CorrelationTrace("<sc.SqlTransaction.Commit|API|Correlation> ObjectID%d#, ActivityID %ls", ObjectID);
  103. TdsParser bestEffortCleanupTarget = null;
  104. RuntimeHelpers.PrepareConstrainedRegions();
  105. try {
  106. #if DEBUG
  107. TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
  108. RuntimeHelpers.PrepareConstrainedRegions();
  109. try {
  110. tdsReliabilitySection.Start();
  111. #else
  112. {
  113. #endif //DEBUG
  114. bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection);
  115. statistics = SqlStatistics.StartTimer(Statistics);
  116. _isFromAPI = true;
  117. _internalTransaction.Commit();
  118. }
  119. #if DEBUG
  120. finally {
  121. tdsReliabilitySection.Stop();
  122. }
  123. #endif //DEBUG
  124. }
  125. catch (System.OutOfMemoryException e) {
  126. _connection.Abort(e);
  127. throw;
  128. }
  129. catch (System.StackOverflowException e) {
  130. _connection.Abort(e);
  131. throw;
  132. }
  133. catch (System.Threading.ThreadAbortException e) {
  134. _connection.Abort(e);
  135. SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
  136. throw;
  137. }
  138. finally {
  139. _isFromAPI = false;
  140. SqlStatistics.StopTimer(statistics);
  141. Bid.ScopeLeave(ref hscp);
  142. }
  143. }
  144. protected override void Dispose(bool disposing) {
  145. if (disposing) {
  146. TdsParser bestEffortCleanupTarget = null;
  147. RuntimeHelpers.PrepareConstrainedRegions();
  148. try {
  149. #if DEBUG
  150. TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
  151. RuntimeHelpers.PrepareConstrainedRegions();
  152. try {
  153. tdsReliabilitySection.Start();
  154. #else
  155. {
  156. #endif //DEBUG
  157. bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection);
  158. if (!IsZombied && !IsYukonPartialZombie) {
  159. _internalTransaction.Dispose();
  160. }
  161. }
  162. #if DEBUG
  163. finally {
  164. tdsReliabilitySection.Stop();
  165. }
  166. #endif //DEBUG
  167. }
  168. catch (System.OutOfMemoryException e) {
  169. _connection.Abort(e);
  170. throw;
  171. }
  172. catch (System.StackOverflowException e) {
  173. _connection.Abort(e);
  174. throw;
  175. }
  176. catch (System.Threading.ThreadAbortException e) {
  177. _connection.Abort(e);
  178. SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
  179. throw;
  180. }
  181. }
  182. base.Dispose(disposing);
  183. }
  184. override public void Rollback() {
  185. if (IsYukonPartialZombie) {
  186. // Put something in the trace in case a customer has an issue
  187. if (Bid.AdvancedOn) {
  188. Bid.Trace("<sc.SqlTransaction.Rollback|ADV> %d# partial zombie no rollback required\n", ObjectID);
  189. }
  190. _internalTransaction = null; // yukon zombification
  191. }
  192. else {
  193. ZombieCheck();
  194. SqlStatistics statistics = null;
  195. IntPtr hscp;
  196. Bid.ScopeEnter(out hscp, "<sc.SqlTransaction.Rollback|API> %d#", ObjectID);
  197. Bid.CorrelationTrace("<sc.SqlTransaction.Rollback|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
  198. TdsParser bestEffortCleanupTarget = null;
  199. RuntimeHelpers.PrepareConstrainedRegions();
  200. try {
  201. #if DEBUG
  202. TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
  203. RuntimeHelpers.PrepareConstrainedRegions();
  204. try {
  205. tdsReliabilitySection.Start();
  206. #else
  207. {
  208. #endif //DEBUG
  209. bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection);
  210. statistics = SqlStatistics.StartTimer(Statistics);
  211. _isFromAPI = true;
  212. _internalTransaction.Rollback();
  213. }
  214. #if DEBUG
  215. finally {
  216. tdsReliabilitySection.Stop();
  217. }
  218. #endif //DEBUG
  219. }
  220. catch (System.OutOfMemoryException e) {
  221. _connection.Abort(e);
  222. throw;
  223. }
  224. catch (System.StackOverflowException e) {
  225. _connection.Abort(e);
  226. throw;
  227. }
  228. catch (System.Threading.ThreadAbortException e) {
  229. _connection.Abort(e);
  230. SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
  231. throw;
  232. }
  233. finally {
  234. _isFromAPI = false;
  235. SqlStatistics.StopTimer(statistics);
  236. Bid.ScopeLeave(ref hscp);
  237. }
  238. }
  239. }
  240. public void Rollback(string transactionName) {
  241. SqlConnection.ExecutePermission.Demand(); // MDAC 81476
  242. ZombieCheck();
  243. SqlStatistics statistics = null;
  244. IntPtr hscp;
  245. Bid.ScopeEnter(out hscp, "<sc.SqlTransaction.Rollback|API> %d# transactionName='%ls'", ObjectID, transactionName);
  246. TdsParser bestEffortCleanupTarget = null;
  247. RuntimeHelpers.PrepareConstrainedRegions();
  248. try {
  249. #if DEBUG
  250. TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
  251. RuntimeHelpers.PrepareConstrainedRegions();
  252. try {
  253. tdsReliabilitySection.Start();
  254. #else
  255. {
  256. #endif //DEBUG
  257. bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection);
  258. statistics = SqlStatistics.StartTimer(Statistics);
  259. _isFromAPI = true;
  260. _internalTransaction.Rollback(transactionName);
  261. }
  262. #if DEBUG
  263. finally {
  264. tdsReliabilitySection.Stop();
  265. }
  266. #endif //DEBUG
  267. }
  268. catch (System.OutOfMemoryException e) {
  269. _connection.Abort(e);
  270. throw;
  271. }
  272. catch (System.StackOverflowException e) {
  273. _connection.Abort(e);
  274. throw;
  275. }
  276. catch (System.Threading.ThreadAbortException e) {
  277. _connection.Abort(e);
  278. SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
  279. throw;
  280. }
  281. finally {
  282. _isFromAPI = false;
  283. SqlStatistics.StopTimer(statistics);
  284. Bid.ScopeLeave(ref hscp);
  285. }
  286. }
  287. public void Save(string savePointName) {
  288. SqlConnection.ExecutePermission.Demand(); // MDAC 81476
  289. ZombieCheck();
  290. SqlStatistics statistics = null;
  291. IntPtr hscp;
  292. Bid.ScopeEnter(out hscp, "<sc.SqlTransaction.Save|API> %d# savePointName='%ls'", ObjectID, savePointName);
  293. TdsParser bestEffortCleanupTarget = null;
  294. RuntimeHelpers.PrepareConstrainedRegions();
  295. try {
  296. #if DEBUG
  297. TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
  298. RuntimeHelpers.PrepareConstrainedRegions();
  299. try {
  300. tdsReliabilitySection.Start();
  301. #else
  302. {
  303. #endif //DEBUG
  304. bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection);
  305. statistics = SqlStatistics.StartTimer(Statistics);
  306. _internalTransaction.Save(savePointName);
  307. }
  308. #if DEBUG
  309. finally {
  310. tdsReliabilitySection.Stop();
  311. }
  312. #endif //DEBUG
  313. }
  314. catch (System.OutOfMemoryException e) {
  315. _connection.Abort(e);
  316. throw;
  317. }
  318. catch (System.StackOverflowException e) {
  319. _connection.Abort(e);
  320. throw;
  321. }
  322. catch (System.Threading.ThreadAbortException e) {
  323. _connection.Abort(e);
  324. SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
  325. throw;
  326. }
  327. finally {
  328. SqlStatistics.StopTimer(statistics);
  329. Bid.ScopeLeave(ref hscp);
  330. }
  331. }
  332. ////////////////////////////////////////////////////////////////////////////////////////
  333. // INTERNAL METHODS
  334. ////////////////////////////////////////////////////////////////////////////////////////
  335. internal void Zombie() {
  336. // SQLBUDT #402544 For Yukon, we have to defer "zombification" until
  337. // we get past the users' next rollback, else we'll
  338. // throw an exception there that is a breaking change.
  339. // Of course, if the connection is aready closed,
  340. // then we're free to zombify...
  341. SqlInternalConnection internalConnection = (_connection.InnerConnection as SqlInternalConnection);
  342. if (null != internalConnection && internalConnection.IsYukonOrNewer && !_isFromAPI) {
  343. if (Bid.AdvancedOn) {
  344. Bid.Trace("<sc.SqlTransaction.Zombie|ADV> %d# yukon deferred zombie\n", ObjectID);
  345. }
  346. }
  347. else {
  348. _internalTransaction = null; // pre-yukon zombification
  349. }
  350. }
  351. ////////////////////////////////////////////////////////////////////////////////////////
  352. // PRIVATE METHODS
  353. ////////////////////////////////////////////////////////////////////////////////////////
  354. private void ZombieCheck() {
  355. // If this transaction has been completed, throw exception since it is unusable.
  356. if (IsZombied) {
  357. if (IsYukonPartialZombie) {
  358. _internalTransaction = null; // yukon zombification
  359. }
  360. throw ADP.TransactionZombied(this);
  361. }
  362. }
  363. }
  364. }