| File: OleDbTransaction.cs | Web Access |
| Project: src\runtime\src\libraries\System.Data.OleDb\src\System.Data.OleDb.csproj (System.Data.OleDb) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Data.Common; using System.Data.ProviderBase; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace System.Data.OleDb { [RequiresDynamicCode(OleDbConnection.TrimWarning)] public sealed class OleDbTransaction : DbTransaction { private readonly OleDbTransaction? _parentTransaction; // strong reference to keep parent alive private readonly System.Data.IsolationLevel _isolationLevel; private WeakReference? _nestedTransaction; // child transactions private WrappedTransaction? _transaction; internal OleDbConnection _parentConnection; private sealed class WrappedTransaction : WrappedIUnknown { private bool _mustComplete; internal WrappedTransaction(UnsafeNativeMethods.ITransactionLocal transaction, int isolevel, out OleDbHResult hr) : base(transaction) { RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { hr = transaction.StartTransaction(isolevel, 0, IntPtr.Zero, out _); if (0 <= hr) { _mustComplete = true; } } } internal bool MustComplete { get { return _mustComplete; } } internal OleDbHResult Abort() { Debug.Assert(_mustComplete, "transaction already completed"); OleDbHResult hr; bool mustRelease = false; RuntimeHelpers.PrepareConstrainedRegions(); try { DangerousAddRef(ref mustRelease); RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { hr = (OleDbHResult)NativeOledbWrapper.ITransactionAbort(DangerousGetHandle()); _mustComplete = false; } } finally { if (mustRelease) { DangerousRelease(); } } return hr; } internal OleDbHResult Commit() { Debug.Assert(_mustComplete, "transaction already completed"); OleDbHResult hr; bool mustRelease = false; RuntimeHelpers.PrepareConstrainedRegions(); try { DangerousAddRef(ref mustRelease); RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { hr = (OleDbHResult)NativeOledbWrapper.ITransactionCommit(DangerousGetHandle()); if ((0 <= (int)hr) || (OleDbHResult.XACT_E_NOTRANSACTION == hr)) { _mustComplete = false; } } } finally { if (mustRelease) { DangerousRelease(); } } return hr; } protected override bool ReleaseHandle() { if (_mustComplete && (IntPtr.Zero != base.handle)) { NativeOledbWrapper.ITransactionAbort(base.handle); _mustComplete = false; } return base.ReleaseHandle(); } } internal OleDbTransaction(OleDbConnection connection, OleDbTransaction? transaction, IsolationLevel isolevel) { _parentConnection = connection; _parentTransaction = transaction; switch (isolevel) { case IsolationLevel.Unspecified: // OLE DB doesn't support this isolevel on local transactions isolevel = IsolationLevel.ReadCommitted; break; case IsolationLevel.Chaos: case IsolationLevel.ReadUncommitted: case IsolationLevel.ReadCommitted: case IsolationLevel.RepeatableRead: case IsolationLevel.Serializable: case IsolationLevel.Snapshot: break; default: throw ADP.InvalidIsolationLevel(isolevel); } _isolationLevel = isolevel; } public new OleDbConnection? Connection { get { return _parentConnection; } } protected override DbConnection? DbConnection { get { return Connection; } } public override IsolationLevel IsolationLevel { get { if (null == _transaction) { throw ADP.TransactionZombied(this); } return _isolationLevel; } } internal OleDbTransaction? Parent { get { return _parentTransaction; } } public OleDbTransaction Begin(IsolationLevel isolevel) { if (null == _transaction) { throw ADP.TransactionZombied(this); } else if ((null != _nestedTransaction) && _nestedTransaction.IsAlive) { throw ADP.ParallelTransactionsNotSupported(Connection!); } // either the connection will be open or this will be a zombie OleDbTransaction transaction = new OleDbTransaction(_parentConnection, this, isolevel); _nestedTransaction = new WeakReference(transaction, false); UnsafeNativeMethods.ITransactionLocal? wrapper = null; try { wrapper = (UnsafeNativeMethods.ITransactionLocal)_transaction.ComWrapper(); transaction.BeginInternal(wrapper); } finally { if (null != wrapper) { Marshal.ReleaseComObject(wrapper); } } return transaction; } public OleDbTransaction Begin() { return Begin(IsolationLevel.ReadCommitted); } internal void BeginInternal(UnsafeNativeMethods.ITransactionLocal transaction) { OleDbHResult hr; _transaction = new WrappedTransaction(transaction, (int)_isolationLevel, out hr); if (hr < 0) { _transaction.Dispose(); _transaction = null; ProcessResults(hr); } } public override void Commit() { if (null == _transaction) { throw ADP.TransactionZombied(this); } CommitInternal(); } private void CommitInternal() { if (null == _transaction) { return; } if (null != _nestedTransaction) { OleDbTransaction? transaction = (OleDbTransaction?)_nestedTransaction.Target; if ((null != transaction) && _nestedTransaction.IsAlive) { transaction.CommitInternal(); } _nestedTransaction = null; } OleDbHResult hr = _transaction.Commit(); if (!_transaction.MustComplete) { _transaction.Dispose(); _transaction = null; DisposeManaged(); } if (hr < 0) { // if an exception is thrown, user can try to commit their transaction again ProcessResults(hr); } } /*public OleDbCommand CreateCommand() { OleDbCommand cmd = Connection.CreateCommand(); cmd.Transaction = this; return cmd; } IDbCommand IDbTransaction.CreateCommand() { return CreateCommand(); }*/ protected override void Dispose(bool disposing) { if (disposing) { DisposeManaged(); RollbackInternal(false); } base.Dispose(disposing); } private void DisposeManaged() { if (null != _parentTransaction) { _parentTransaction._nestedTransaction = null; //_parentTransaction = null; } else { _parentConnection?.LocalTransaction = null; } _parentConnection = null!; } private void ProcessResults(OleDbHResult hr) { Exception? e = OleDbConnection.ProcessResults(hr, _parentConnection); if (null != e) { throw e; } } public override void Rollback() { if (null == _transaction) { throw ADP.TransactionZombied(this); } DisposeManaged(); RollbackInternal(true); // no recover if this throws an exception } /*protected virtual*/ internal OleDbHResult RollbackInternal(bool exceptionHandling) { OleDbHResult hr = 0; if (null != _transaction) { if (null != _nestedTransaction) { OleDbTransaction? transaction = (OleDbTransaction?)_nestedTransaction.Target; if ((null != transaction) && _nestedTransaction.IsAlive) { hr = transaction.RollbackInternal(exceptionHandling); if (exceptionHandling && (hr < 0)) { SafeNativeMethods.Wrapper.ClearErrorInfo(); return hr; } } _nestedTransaction = null; } hr = _transaction.Abort(); _transaction.Dispose(); _transaction = null; if (hr < 0) { if (exceptionHandling) { ProcessResults(hr); } else { SafeNativeMethods.Wrapper.ClearErrorInfo(); } } } return hr; } internal static OleDbTransaction TransactionLast(OleDbTransaction head) { if (null != head._nestedTransaction) { OleDbTransaction? current = (OleDbTransaction?)head._nestedTransaction.Target; if ((null != current) && head._nestedTransaction.IsAlive) { return TransactionLast(current); } } return head; } internal static OleDbTransaction? TransactionUpdate(OleDbTransaction? transaction) { if ((null != transaction) && (null == transaction._transaction)) { return null; } return transaction; } } }