/********************************************************
* ADO.NET 2.0 Data Provider for SQLite Version 3.X
* Written by Robert Simpson (robert@blackcastlesoft.com)
*
* Released to the public domain, use at your own risk!
********************************************************/
namespace System.Data.SQLite
{
using System;
using System.Data;
using System.Data.Common;
///////////////////////////////////////////////////////////////////////////////////////////////////
///
/// Base class used by to implement DbTransaction for SQLite.
///
public abstract class SQLiteTransactionBase : DbTransaction
{
///
/// The connection to which this transaction is bound.
///
internal SQLiteConnection _cnn;
///
/// Matches the version of the connection.
///
internal int _version;
///
/// The isolation level for this transaction.
///
private IsolationLevel _level;
///////////////////////////////////////////////////////////////////////////////////////////////
///
/// Constructs the transaction object, binding it to the supplied connection
///
/// The connection to open a transaction on
/// TRUE to defer the writelock, or FALSE to lock immediately
internal SQLiteTransactionBase(SQLiteConnection connection, bool deferredLock)
{
_cnn = connection;
_version = _cnn._version;
_level = (deferredLock == true) ?
SQLiteConnection.DeferredIsolationLevel :
SQLiteConnection.ImmediateIsolationLevel;
Begin(deferredLock);
}
///////////////////////////////////////////////////////////////////////////////////////////////
///
/// Gets the isolation level of the transaction. SQLite only supports Serializable transactions.
///
public override IsolationLevel IsolationLevel
{
get { CheckDisposed(); return _level; }
}
///////////////////////////////////////////////////////////////////////////////////////////////
#region IDisposable "Pattern" Members
private bool disposed;
private void CheckDisposed() /* throw */
{
#if THROW_ON_DISPOSED
if (disposed)
{
throw new ObjectDisposedException(
typeof(SQLiteTransactionBase).Name);
}
#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////
///
/// Disposes the transaction. If it is currently active, any changes are rolled back.
///
protected override void Dispose(bool disposing)
{
try
{
if (!disposed)
{
if (disposing)
{
////////////////////////////////////
// dispose managed resources here...
////////////////////////////////////
if (IsValid(false))
{
IssueRollback(false);
}
}
//////////////////////////////////////
// release unmanaged resources here...
//////////////////////////////////////
}
}
finally
{
base.Dispose(disposing);
//
// NOTE: Everything should be fully disposed at this point.
//
disposed = true;
}
}
#endregion
///////////////////////////////////////////////////////////////////////////////////////////////
///
/// Returns the underlying connection to which this transaction applies.
///
public new SQLiteConnection Connection
{
get { CheckDisposed(); return _cnn; }
}
///////////////////////////////////////////////////////////////////////////////////////////////
///
/// Forwards to the local Connection property
///
protected override DbConnection DbConnection
{
get { return Connection; }
}
///////////////////////////////////////////////////////////////////////////////////////////////
///
/// Rolls back the active transaction.
///
public override void Rollback()
{
CheckDisposed();
SQLiteConnection.Check(_cnn);
IsValid(true);
IssueRollback(true);
}
///////////////////////////////////////////////////////////////////////////////////////////////
///
/// Attempts to start a transaction. An exception will be thrown if the transaction cannot
/// be started for any reason.
///
/// TRUE to defer the writelock, or FALSE to lock immediately
protected abstract void Begin(bool deferredLock);
///////////////////////////////////////////////////////////////////////////////////////////////
///
/// Issue a ROLLBACK command against the database connection,
/// optionally re-throwing any caught exception.
///
///
/// Non-zero to re-throw caught exceptions.
///
protected abstract void IssueRollback(bool throwError);
///////////////////////////////////////////////////////////////////////////////////////////////
///
/// Checks the state of this transaction, optionally throwing an exception if a state
/// inconsistency is found.
///
///
/// Non-zero to throw an exception if a state inconsistency is found.
///
///
/// Non-zero if this transaction is valid; otherwise, false.
///
internal bool IsValid(bool throwError)
{
if (_cnn == null)
{
if (throwError == true) throw new ArgumentNullException("No connection associated with this transaction");
else return false;
}
if (_cnn._version != _version)
{
if (throwError == true) throw new SQLiteException("The connection was closed and re-opened, changes were already rolled back");
else return false;
}
if (_cnn.State != ConnectionState.Open)
{
if (throwError == true) throw new SQLiteException("Connection was closed");
else return false;
}
if (_cnn._transactionLevel == 0 || _cnn._sql.AutoCommit == true)
{
_cnn._transactionLevel = 0; // Make sure the transaction level is reset before returning
if (throwError == true) throw new SQLiteException("No transaction is active on this connection");
else return false;
}
return true;
}
}
}