536cd135cc
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
167 lines
6.6 KiB
C#
167 lines
6.6 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="OdbcTransaction.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>
|
|
//------------------------------------------------------------------------------
|
|
|
|
using System;
|
|
using System.Data;
|
|
using System.Data.Common;
|
|
using System.Threading;
|
|
|
|
namespace System.Data.Odbc
|
|
{
|
|
public sealed class OdbcTransaction : DbTransaction {
|
|
private OdbcConnection _connection;
|
|
private IsolationLevel _isolevel = IsolationLevel.Unspecified;
|
|
private OdbcConnectionHandle _handle;
|
|
|
|
internal OdbcTransaction(OdbcConnection connection, IsolationLevel isolevel, OdbcConnectionHandle handle) {
|
|
OdbcConnection.VerifyExecutePermission();
|
|
|
|
_connection = connection;
|
|
_isolevel = isolevel;
|
|
_handle = handle;
|
|
}
|
|
|
|
new public OdbcConnection Connection { // MDAC 66655
|
|
get {
|
|
return _connection;
|
|
}
|
|
}
|
|
|
|
override protected DbConnection DbConnection { // MDAC 66655
|
|
get {
|
|
return Connection;
|
|
}
|
|
}
|
|
|
|
override public IsolationLevel IsolationLevel {
|
|
get {
|
|
OdbcConnection connection = _connection;
|
|
if (null == connection ) {
|
|
throw ADP.TransactionZombied(this);
|
|
}
|
|
|
|
//We need to query for the case where the user didn't set the isolevel
|
|
//BeginTransaction(), but we should also query to see if the driver
|
|
//"rolled" the level to a higher supported one...
|
|
if(IsolationLevel.Unspecified == _isolevel) {
|
|
//Get the isolation level
|
|
int sql_iso= connection .GetConnectAttr(ODBC32.SQL_ATTR.TXN_ISOLATION, ODBC32.HANDLER.THROW);
|
|
switch((ODBC32.SQL_TRANSACTION)sql_iso) {
|
|
case ODBC32.SQL_TRANSACTION.READ_UNCOMMITTED:
|
|
_isolevel = IsolationLevel.ReadUncommitted;
|
|
break;
|
|
case ODBC32.SQL_TRANSACTION.READ_COMMITTED:
|
|
_isolevel = IsolationLevel.ReadCommitted;
|
|
break;
|
|
case ODBC32.SQL_TRANSACTION.REPEATABLE_READ:
|
|
_isolevel = IsolationLevel.RepeatableRead;
|
|
break;
|
|
case ODBC32.SQL_TRANSACTION.SERIALIZABLE:
|
|
_isolevel = IsolationLevel.Serializable;
|
|
break;
|
|
case ODBC32.SQL_TRANSACTION.SNAPSHOT:
|
|
_isolevel = IsolationLevel.Snapshot;
|
|
break;
|
|
default:
|
|
throw ODBC.NoMappingForSqlTransactionLevel(sql_iso);
|
|
};
|
|
}
|
|
return _isolevel;
|
|
}
|
|
}
|
|
|
|
override public void Commit() {
|
|
OdbcConnection.ExecutePermission.Demand(); // MDAC 81476
|
|
|
|
OdbcConnection connection = _connection;
|
|
if (null == connection) {
|
|
throw ADP.TransactionZombied(this);
|
|
}
|
|
|
|
connection.CheckState(ADP.CommitTransaction); // MDAC 68289
|
|
|
|
//Note: SQLEndTran success if not actually in a transaction, so we have to throw
|
|
//since the IDbTransaciton spec indicates this is an error for the managed packages
|
|
if(null == _handle) {
|
|
throw ODBC.NotInTransaction();
|
|
}
|
|
|
|
ODBC32.RetCode retcode = _handle.CompleteTransaction(ODBC32.SQL_COMMIT);
|
|
if (retcode == ODBC32.RetCode.ERROR) {
|
|
//If an error has occurred, we will throw an exception in HandleError,
|
|
//and leave the transaction active for the user to retry
|
|
connection.HandleError(_handle, retcode);
|
|
}
|
|
|
|
//Transaction is complete...
|
|
connection.LocalTransaction = null;
|
|
_connection = null;
|
|
_handle = null;
|
|
|
|
}
|
|
|
|
protected override void Dispose(bool disposing) {
|
|
if (disposing) {
|
|
OdbcConnectionHandle handle = _handle;
|
|
_handle = null;
|
|
if (null != handle){
|
|
try{
|
|
ODBC32.RetCode retcode = handle.CompleteTransaction(ODBC32.SQL_ROLLBACK);
|
|
if (retcode == ODBC32.RetCode.ERROR) {
|
|
//don't throw an exception here, but trace it so it can be logged
|
|
if (_connection != null) {
|
|
Exception e = _connection.HandleErrorNoThrow(handle, retcode);
|
|
ADP.TraceExceptionWithoutRethrow(e);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e){
|
|
//
|
|
if (!ADP.IsCatchableExceptionType(e)) {
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
if (_connection != null) {
|
|
if (_connection.IsOpen) {
|
|
_connection.LocalTransaction = null;
|
|
}
|
|
}
|
|
_connection = null;
|
|
_isolevel = IsolationLevel.Unspecified;
|
|
}
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
override public void Rollback() {
|
|
OdbcConnection connection = _connection;
|
|
if (null == connection) {
|
|
throw ADP.TransactionZombied(this);
|
|
}
|
|
connection.CheckState(ADP.RollbackTransaction); // MDAC 68289
|
|
|
|
//Note: SQLEndTran success if not actually in a transaction, so we have to throw
|
|
//since the IDbTransaciton spec indicates this is an error for the managed packages
|
|
if(null == _handle) {
|
|
throw ODBC.NotInTransaction();
|
|
}
|
|
|
|
ODBC32.RetCode retcode = _handle.CompleteTransaction(ODBC32.SQL_ROLLBACK);
|
|
if (retcode == ODBC32.RetCode.ERROR) {
|
|
//If an error has occurred, we will throw an exception in HandleError,
|
|
//and leave the transaction active for the user to retry
|
|
connection.HandleError(_handle, retcode);
|
|
}
|
|
connection.LocalTransaction = null;
|
|
_connection = null;
|
|
_handle = null;
|
|
}
|
|
}
|
|
}
|
|
|