|
//------------------------------------------------------------------------------
// <copyright file="OleDbTransaction.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
// <owner current="true" primary="false">Microsoft</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;
}
}
}
|