355 lines
13 KiB
C#
355 lines
13 KiB
C#
|
//------------------------------------------------------------------------------
|
||
|
// <copyright file="OleDbTransaction.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.OleDb {
|
||
|
|
||
|
using System.Data;
|
||
|
using System.Data.Common;
|
||
|
using System.Data.ProviderBase;
|
||
|
using System.Diagnostics;
|
||
|
using System.Runtime.CompilerServices;
|
||
|
using System.Runtime.ConstrainedExecution;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using System.Threading;
|
||
|
|
||
|
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 static int _objectTypeCount; // Bid counter
|
||
|
internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
|
||
|
|
||
|
private sealed class WrappedTransaction : WrappedIUnknown {
|
||
|
|
||
|
private bool _mustComplete;
|
||
|
|
||
|
internal WrappedTransaction(UnsafeNativeMethods.ITransactionLocal transaction, int isolevel, out OleDbHResult hr) : base(transaction) {
|
||
|
int transactionLevel = 0;
|
||
|
Bid.Trace("<oledb.ITransactionLocal.StartTransaction|API|OLEDB>\n");
|
||
|
RuntimeHelpers.PrepareConstrainedRegions();
|
||
|
try { } finally {
|
||
|
hr = transaction.StartTransaction(isolevel, 0, IntPtr.Zero, out transactionLevel);
|
||
|
if (0 <= hr) {
|
||
|
_mustComplete = true;
|
||
|
}
|
||
|
}
|
||
|
Bid.Trace("<oledb.ITransactionLocal.StartTransaction|API|OLEDB|RET> %08X{HRESULT}\n", hr);
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
|
||
|
Bid.Trace("<oledb.ITransactionLocal.Abort|API|OLEDB> handle=%p\n", base.handle);
|
||
|
RuntimeHelpers.PrepareConstrainedRegions();
|
||
|
try { } finally {
|
||
|
hr = (OleDbHResult)NativeOledbWrapper.ITransactionAbort(DangerousGetHandle());
|
||
|
_mustComplete = false;
|
||
|
}
|
||
|
Bid.Trace("<oledb.ITransactionLocal.Abort|API|OLEDB|RET> %08X{HRESULT}\n", hr);
|
||
|
}
|
||
|
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);
|
||
|
|
||
|
Bid.Trace("<oledb.ITransactionLocal.Commit|API|OLEDB> handle=%p\n", base.handle);
|
||
|
RuntimeHelpers.PrepareConstrainedRegions();
|
||
|
try { } finally {
|
||
|
hr = (OleDbHResult)NativeOledbWrapper.ITransactionCommit(DangerousGetHandle());
|
||
|
if ((0 <= (int)hr) || (OleDbHResult.XACT_E_NOTRANSACTION == hr)) {
|
||
|
_mustComplete = false;
|
||
|
}
|
||
|
}
|
||
|
Bid.Trace("<oledb.ITransactionLocal.Commit|API|OLEDB|RET> %08X{HRESULT}\n", hr);
|
||
|
}
|
||
|
finally {
|
||
|
if (mustRelease) {
|
||
|
DangerousRelease();
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
override protected bool ReleaseHandle() {
|
||
|
if (_mustComplete && (IntPtr.Zero != base.handle)) {
|
||
|
Bid.Trace("<oledb.ITransactionLocal.Abort|API|OLEDB|INFO> handle=%p\n", base.handle);
|
||
|
OleDbHResult hr = (OleDbHResult)NativeOledbWrapper.ITransactionAbort(base.handle);
|
||
|
_mustComplete = false;
|
||
|
Bid.Trace("<oledb.ITransactionLocal.Abort|API|OLEDB|INFO|RET> %08X{HRESULT}\n", hr);
|
||
|
}
|
||
|
return base.ReleaseHandle();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal OleDbTransaction(OleDbConnection connection, OleDbTransaction transaction, IsolationLevel isolevel) {
|
||
|
OleDbConnection.VerifyExecutePermission();
|
||
|
|
||
|
_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); // MDAC 74269
|
||
|
}
|
||
|
_isolationLevel = isolevel;
|
||
|
}
|
||
|
|
||
|
new public OleDbConnection Connection { // MDAC 66655
|
||
|
get {
|
||
|
return _parentConnection;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
override protected DbConnection DbConnection {
|
||
|
get {
|
||
|
return Connection;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
override public IsolationLevel IsolationLevel {
|
||
|
get {
|
||
|
if (null == _transaction) {
|
||
|
throw ADP.TransactionZombied(this);
|
||
|
}
|
||
|
return _isolationLevel;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal int ObjectID {
|
||
|
get {
|
||
|
return _objectID;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal OleDbTransaction Parent {
|
||
|
get {
|
||
|
return _parentTransaction;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public OleDbTransaction Begin(IsolationLevel isolevel) {
|
||
|
OleDbConnection.ExecutePermission.Demand(); // MDAC 81476
|
||
|
|
||
|
IntPtr hscp;
|
||
|
Bid.ScopeEnter(out hscp, "<oledb.OleDbTransaction.Begin|API> %d#, isolevel=%d{IsolationLevel}", ObjectID, (int)isolevel);
|
||
|
try {
|
||
|
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;
|
||
|
}
|
||
|
finally {
|
||
|
Bid.ScopeLeave(ref hscp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
override public void Commit() {
|
||
|
OleDbConnection.ExecutePermission.Demand(); // MDAC 81476
|
||
|
|
||
|
IntPtr hscp;
|
||
|
Bid.ScopeEnter(out hscp, "<oledb.OleDbTransaction.Commit|API> %d#", ObjectID);
|
||
|
try {
|
||
|
if (null == _transaction) {
|
||
|
throw ADP.TransactionZombied(this);
|
||
|
}
|
||
|
CommitInternal();
|
||
|
}
|
||
|
finally {
|
||
|
Bid.ScopeLeave(ref hscp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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() { // MDAC 68309
|
||
|
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 if (null != _parentConnection) { // MDAC 67287
|
||
|
_parentConnection.LocalTransaction = null;
|
||
|
}
|
||
|
_parentConnection = null;
|
||
|
}
|
||
|
|
||
|
private void ProcessResults(OleDbHResult hr) {
|
||
|
Exception e = OleDbConnection.ProcessResults(hr, _parentConnection, this);
|
||
|
if (null != e) { throw e; }
|
||
|
}
|
||
|
|
||
|
override public void Rollback() {
|
||
|
IntPtr hscp;
|
||
|
Bid.ScopeEnter(out hscp, "<oledb.OleDbTransaction.Rollback|API> %d#", ObjectID);
|
||
|
try {
|
||
|
if (null == _transaction) {
|
||
|
throw ADP.TransactionZombied(this);
|
||
|
}
|
||
|
DisposeManaged();
|
||
|
RollbackInternal(true); // no recover if this throws an exception
|
||
|
}
|
||
|
finally {
|
||
|
Bid.ScopeLeave(ref hscp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*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;
|
||
|
}
|
||
|
|
||
|
static internal 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;
|
||
|
}
|
||
|
|
||
|
static internal OleDbTransaction TransactionUpdate(OleDbTransaction transaction) {
|
||
|
if ((null != transaction) && (null == transaction._transaction)) {
|
||
|
return null;
|
||
|
}
|
||
|
return transaction;
|
||
|
}
|
||
|
}
|
||
|
}
|