1 //------------------------------------------------------------------------------ 2 // <copyright file="OleDbTransaction.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // <owner current="true" primary="true">Microsoft</owner> 6 // <owner current="true" primary="false">Microsoft</owner> 7 //------------------------------------------------------------------------------ 8 9 namespace System.Data.OleDb { 10 11 using System.Data; 12 using System.Data.Common; 13 using System.Data.ProviderBase; 14 using System.Diagnostics; 15 using System.Runtime.CompilerServices; 16 using System.Runtime.ConstrainedExecution; 17 using System.Runtime.InteropServices; 18 using System.Threading; 19 20 public sealed class OleDbTransaction : DbTransaction { 21 22 private readonly OleDbTransaction _parentTransaction; // strong reference to keep parent alive 23 private readonly System.Data.IsolationLevel _isolationLevel; 24 25 private WeakReference _nestedTransaction; // child transactions 26 private WrappedTransaction _transaction; 27 28 internal OleDbConnection _parentConnection; 29 30 private static int _objectTypeCount; // Bid counter 31 internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); 32 33 private sealed class WrappedTransaction : WrappedIUnknown { 34 35 private bool _mustComplete; 36 WrappedTransaction(UnsafeNativeMethods.ITransactionLocal transaction, int isolevel, out OleDbHResult hr)37 internal WrappedTransaction(UnsafeNativeMethods.ITransactionLocal transaction, int isolevel, out OleDbHResult hr) : base(transaction) { 38 int transactionLevel = 0; 39 Bid.Trace("<oledb.ITransactionLocal.StartTransaction|API|OLEDB>\n"); 40 RuntimeHelpers.PrepareConstrainedRegions(); 41 try { } finally { 42 hr = transaction.StartTransaction(isolevel, 0, IntPtr.Zero, out transactionLevel); 43 if (0 <= hr) { 44 _mustComplete = true; 45 } 46 } 47 Bid.Trace("<oledb.ITransactionLocal.StartTransaction|API|OLEDB|RET> %08X{HRESULT}\n", hr); 48 } 49 50 internal bool MustComplete { 51 get { return _mustComplete; } 52 } 53 Abort()54 internal OleDbHResult Abort() { 55 Debug.Assert(_mustComplete, "transaction already completed"); 56 OleDbHResult hr; 57 bool mustRelease = false; 58 RuntimeHelpers.PrepareConstrainedRegions(); 59 try { 60 DangerousAddRef(ref mustRelease); 61 62 Bid.Trace("<oledb.ITransactionLocal.Abort|API|OLEDB> handle=%p\n", base.handle); 63 RuntimeHelpers.PrepareConstrainedRegions(); 64 try { } finally { 65 hr = (OleDbHResult)NativeOledbWrapper.ITransactionAbort(DangerousGetHandle()); 66 _mustComplete = false; 67 } 68 Bid.Trace("<oledb.ITransactionLocal.Abort|API|OLEDB|RET> %08X{HRESULT}\n", hr); 69 } 70 finally { 71 if (mustRelease) { 72 DangerousRelease(); 73 } 74 } 75 return hr; 76 } 77 Commit()78 internal OleDbHResult Commit() { 79 Debug.Assert(_mustComplete, "transaction already completed"); 80 OleDbHResult hr; 81 bool mustRelease = false; 82 RuntimeHelpers.PrepareConstrainedRegions(); 83 try { 84 DangerousAddRef(ref mustRelease); 85 86 Bid.Trace("<oledb.ITransactionLocal.Commit|API|OLEDB> handle=%p\n", base.handle); 87 RuntimeHelpers.PrepareConstrainedRegions(); 88 try { } finally { 89 hr = (OleDbHResult)NativeOledbWrapper.ITransactionCommit(DangerousGetHandle()); 90 if ((0 <= (int)hr) || (OleDbHResult.XACT_E_NOTRANSACTION == hr)) { 91 _mustComplete = false; 92 } 93 } 94 Bid.Trace("<oledb.ITransactionLocal.Commit|API|OLEDB|RET> %08X{HRESULT}\n", hr); 95 } 96 finally { 97 if (mustRelease) { 98 DangerousRelease(); 99 } 100 } 101 return hr; 102 } 103 ReleaseHandle()104 override protected bool ReleaseHandle() { 105 if (_mustComplete && (IntPtr.Zero != base.handle)) { 106 Bid.Trace("<oledb.ITransactionLocal.Abort|API|OLEDB|INFO> handle=%p\n", base.handle); 107 OleDbHResult hr = (OleDbHResult)NativeOledbWrapper.ITransactionAbort(base.handle); 108 _mustComplete = false; 109 Bid.Trace("<oledb.ITransactionLocal.Abort|API|OLEDB|INFO|RET> %08X{HRESULT}\n", hr); 110 } 111 return base.ReleaseHandle(); 112 } 113 } 114 OleDbTransaction(OleDbConnection connection, OleDbTransaction transaction, IsolationLevel isolevel)115 internal OleDbTransaction(OleDbConnection connection, OleDbTransaction transaction, IsolationLevel isolevel) { 116 OleDbConnection.VerifyExecutePermission(); 117 118 _parentConnection = connection; 119 _parentTransaction = transaction; 120 121 switch(isolevel) { 122 case IsolationLevel.Unspecified: // OLE DB doesn't support this isolevel on local transactions 123 isolevel = IsolationLevel.ReadCommitted; 124 break; 125 case IsolationLevel.Chaos: 126 case IsolationLevel.ReadUncommitted: 127 case IsolationLevel.ReadCommitted: 128 case IsolationLevel.RepeatableRead: 129 case IsolationLevel.Serializable: 130 case IsolationLevel.Snapshot: 131 break; 132 default: 133 throw ADP.InvalidIsolationLevel(isolevel); // MDAC 74269 134 } 135 _isolationLevel = isolevel; 136 } 137 138 new public OleDbConnection Connection { // MDAC 66655 139 get { 140 return _parentConnection; 141 } 142 } 143 144 override protected DbConnection DbConnection { 145 get { 146 return Connection; 147 } 148 } 149 150 override public IsolationLevel IsolationLevel { 151 get { 152 if (null == _transaction) { 153 throw ADP.TransactionZombied(this); 154 } 155 return _isolationLevel; 156 } 157 } 158 159 internal int ObjectID { 160 get { 161 return _objectID; 162 } 163 } 164 165 internal OleDbTransaction Parent { 166 get { 167 return _parentTransaction; 168 } 169 } 170 Begin(IsolationLevel isolevel)171 public OleDbTransaction Begin(IsolationLevel isolevel) { 172 OleDbConnection.ExecutePermission.Demand(); // MDAC 81476 173 174 IntPtr hscp; 175 Bid.ScopeEnter(out hscp, "<oledb.OleDbTransaction.Begin|API> %d#, isolevel=%d{IsolationLevel}", ObjectID, (int)isolevel); 176 try { 177 if (null == _transaction) { 178 throw ADP.TransactionZombied(this); 179 } 180 else if ((null != _nestedTransaction) && _nestedTransaction.IsAlive) { 181 throw ADP.ParallelTransactionsNotSupported(Connection); 182 } 183 // either the connection will be open or this will be a zombie 184 185 OleDbTransaction transaction = new OleDbTransaction(_parentConnection, this, isolevel); 186 _nestedTransaction = new WeakReference(transaction, false); 187 188 UnsafeNativeMethods.ITransactionLocal wrapper = null; 189 try { 190 wrapper = (UnsafeNativeMethods.ITransactionLocal)_transaction.ComWrapper(); 191 transaction.BeginInternal(wrapper); 192 } 193 finally { 194 if (null != wrapper) { 195 Marshal.ReleaseComObject(wrapper); 196 } 197 } 198 return transaction; 199 } 200 finally { 201 Bid.ScopeLeave(ref hscp); 202 } 203 } 204 Begin()205 public OleDbTransaction Begin() { 206 return Begin(IsolationLevel.ReadCommitted); 207 } 208 BeginInternal(UnsafeNativeMethods.ITransactionLocal transaction)209 internal void BeginInternal(UnsafeNativeMethods.ITransactionLocal transaction) { 210 OleDbHResult hr; 211 _transaction = new WrappedTransaction(transaction, (int) _isolationLevel, out hr); 212 if (hr < 0) { 213 _transaction.Dispose(); 214 _transaction = null; 215 ProcessResults(hr); 216 } 217 } 218 Commit()219 override public void Commit() { 220 OleDbConnection.ExecutePermission.Demand(); // MDAC 81476 221 222 IntPtr hscp; 223 Bid.ScopeEnter(out hscp, "<oledb.OleDbTransaction.Commit|API> %d#", ObjectID); 224 try { 225 if (null == _transaction) { 226 throw ADP.TransactionZombied(this); 227 } 228 CommitInternal(); 229 } 230 finally { 231 Bid.ScopeLeave(ref hscp); 232 } 233 } 234 CommitInternal()235 private void CommitInternal() { 236 if (null == _transaction) { 237 return; 238 } 239 if (null != _nestedTransaction) { 240 OleDbTransaction transaction = (OleDbTransaction) _nestedTransaction.Target; 241 if ((null != transaction) && _nestedTransaction.IsAlive) { 242 transaction.CommitInternal(); 243 } 244 _nestedTransaction = null; 245 } 246 OleDbHResult hr = _transaction.Commit(); 247 if (!_transaction.MustComplete) { 248 _transaction.Dispose(); 249 _transaction = null; 250 251 DisposeManaged(); 252 } 253 if (hr < 0) { 254 // if an exception is thrown, user can try to commit their transaction again 255 ProcessResults(hr); 256 } 257 } 258 259 /*public OleDbCommand CreateCommand() { // MDAC 68309 260 OleDbCommand cmd = Connection.CreateCommand(); 261 cmd.Transaction = this; 262 return cmd; 263 } 264 265 IDbCommand IDbTransaction.CreateCommand() { 266 return CreateCommand(); 267 }*/ 268 Dispose(bool disposing)269 protected override void Dispose(bool disposing) { 270 if (disposing) { 271 DisposeManaged(); 272 RollbackInternal(false); 273 } 274 base.Dispose(disposing); 275 } 276 DisposeManaged()277 private void DisposeManaged() { 278 if (null != _parentTransaction) { 279 _parentTransaction._nestedTransaction = null; 280 //_parentTransaction = null; 281 } 282 else if (null != _parentConnection) { // MDAC 67287 283 _parentConnection.LocalTransaction = null; 284 } 285 _parentConnection = null; 286 } 287 ProcessResults(OleDbHResult hr)288 private void ProcessResults(OleDbHResult hr) { 289 Exception e = OleDbConnection.ProcessResults(hr, _parentConnection, this); 290 if (null != e) { throw e; } 291 } 292 Rollback()293 override public void Rollback() { 294 IntPtr hscp; 295 Bid.ScopeEnter(out hscp, "<oledb.OleDbTransaction.Rollback|API> %d#", ObjectID); 296 try { 297 if (null == _transaction) { 298 throw ADP.TransactionZombied(this); 299 } 300 DisposeManaged(); 301 RollbackInternal(true); // no recover if this throws an exception 302 } 303 finally { 304 Bid.ScopeLeave(ref hscp); 305 } 306 } 307 RollbackInternal(bool exceptionHandling)308 /*protected virtual*/internal OleDbHResult RollbackInternal(bool exceptionHandling) { 309 OleDbHResult hr = 0; 310 if (null != _transaction) { 311 if (null != _nestedTransaction) { 312 OleDbTransaction transaction = (OleDbTransaction) _nestedTransaction.Target; 313 if ((null != transaction) && _nestedTransaction.IsAlive) { 314 hr = transaction.RollbackInternal(exceptionHandling); 315 if (exceptionHandling && (hr < 0)) { 316 SafeNativeMethods.Wrapper.ClearErrorInfo(); 317 return hr; 318 } 319 } 320 _nestedTransaction = null; 321 } 322 hr = _transaction.Abort(); 323 _transaction.Dispose(); 324 _transaction = null; 325 if (hr < 0) { 326 if (exceptionHandling) { 327 ProcessResults(hr); 328 } 329 else { 330 SafeNativeMethods.Wrapper.ClearErrorInfo(); 331 } 332 } 333 } 334 return hr; 335 } 336 TransactionLast(OleDbTransaction head)337 static internal OleDbTransaction TransactionLast(OleDbTransaction head) { 338 if (null != head._nestedTransaction) { 339 OleDbTransaction current = (OleDbTransaction) head._nestedTransaction.Target; 340 if ((null != current) && head._nestedTransaction.IsAlive) { 341 return TransactionLast(current); 342 } 343 } 344 return head; 345 } 346 TransactionUpdate(OleDbTransaction transaction)347 static internal OleDbTransaction TransactionUpdate(OleDbTransaction transaction) { 348 if ((null != transaction) && (null == transaction._transaction)) { 349 return null; 350 } 351 return transaction; 352 } 353 } 354 } 355