/******************************************************** * 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; } } }