/* * Firebird ADO.NET Data provider for .NET and Mono * * The contents of this file are subject to the Initial * Developer's Public License Version 1.0 (the "License"); * you may not use this file except in compliance with the * License. You may obtain a copy of the License at * http://www.firebirdsql.org/index.php?op=doc&id=idpl * * Software distributed under the License is distributed on * an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either * express or implied. See the License for the specific * language governing rights and limitations under the License. * * Copyright (c) 2002, 2005 Carlos Guzman Alvarez * All Rights Reserved. */ using System; using System.Data; using System.Collections; using FirebirdSql.Data.Common; namespace FirebirdSql.Data.Firebird { /// public sealed class FbTransaction : MarshalByRefObject, IDbTransaction, IDisposable { #region Fields private ITransaction transaction; private FbConnection connection; private IsolationLevel isolationLevel; private bool disposed; private bool isUpdated; #endregion #region Properties IDbConnection IDbTransaction.Connection { get { return this.Connection; } } /// public FbConnection Connection { get { if (!this.isUpdated) { return this.connection; } else { return null; } } } /// public IsolationLevel IsolationLevel { get { return this.isolationLevel; } } #endregion #region Internal Properties internal ITransaction Transaction { get { return this.transaction; } } internal bool IsUpdated { get { return this.isUpdated; } } #endregion #region Constructors internal FbTransaction(FbConnection connection) : this(connection, IsolationLevel.ReadCommitted) { } internal FbTransaction(FbConnection connection, IsolationLevel il) { this.isolationLevel = il; this.connection = connection; } #endregion #region Finalizer /// ~FbTransaction() { this.Dispose(false); } #endregion #region IDisposable Methods /// public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { lock (this) { if (!this.disposed) { try { // release any unmanaged resources if (this.transaction != null) { if ((this.transaction.State == TransactionState.TransactionStarted || this.transaction.State == TransactionState.TransactionPrepared) && !this.isUpdated) { this.transaction.Dispose(); this.transaction = null; } } // release any managed resources if (disposing) { this.connection = null; this.transaction = null; } } finally { this.isUpdated = true; this.disposed = true; } } } } #endregion #region Methods /// public void Commit() { lock (this) { if (this.isUpdated) { throw new InvalidOperationException("This Transaction has completed; it is no longer usable."); } try { this.transaction.Commit(); this.UpdateTransaction(); } catch (IscException ex) { throw new FbException(ex.Message, ex); } } } /// public void Rollback() { lock (this) { if (this.isUpdated) { throw new InvalidOperationException("This Transaction has completed; it is no longer usable."); } try { this.transaction.Rollback(); this.UpdateTransaction(); } catch (IscException ex) { throw new FbException(ex.Message, ex); } } } /// public void Save(string savePointName) { lock (this) { if (savePointName == null) { throw new ArgumentException("No transaction name was be specified."); } else { if (savePointName.Length == 0) { throw new ArgumentException("No transaction name was be specified."); } } if (this.isUpdated) { throw new InvalidOperationException("This Transaction has completed; it is no longer usable."); } try { FbCommand command = new FbCommand( "SAVEPOINT " + savePointName, this.connection, this); command.ExecuteNonQuery(); command.Dispose(); } catch (IscException ex) { throw new FbException(ex.Message, ex); } } } /// public void Commit(string savePointName) { lock (this) { if (savePointName == null) { throw new ArgumentException("No transaction name was be specified."); } else { if (savePointName.Length == 0) { throw new ArgumentException("No transaction name was be specified."); } } if (this.isUpdated) { throw new InvalidOperationException("This Transaction has completed; it is no longer usable."); } try { FbCommand command = new FbCommand( "RELEASE SAVEPOINT " + savePointName, this.connection, this); command.ExecuteNonQuery(); command.Dispose(); } catch (IscException ex) { throw new FbException(ex.Message, ex); } } } /// public void Rollback(string savePointName) { lock (this) { if (savePointName == null) { throw new ArgumentException("No transaction name was be specified."); } else { if (savePointName.Length == 0) { throw new ArgumentException("No transaction name was be specified."); } } if (this.isUpdated) { throw new InvalidOperationException("This Transaction has completed; it is no longer usable."); } try { FbCommand command = new FbCommand( "ROLLBACK WORK TO SAVEPOINT " + savePointName, this.connection, this); command.ExecuteNonQuery(); command.Dispose(); } catch (IscException ex) { throw new FbException(ex.Message, ex); } } } /// public void CommitRetaining() { lock (this) { if (this.isUpdated) { throw new InvalidOperationException("This Transaction has completed; it is no longer usable."); } try { this.transaction.CommitRetaining(); } catch (IscException ex) { throw new FbException(ex.Message, ex); } } } /// public void RollbackRetaining() { lock (this) { if (this.isUpdated) { throw new InvalidOperationException("This Transaction has completed; it is no longer usable."); } try { this.transaction.RollbackRetaining(); } catch (IscException ex) { throw new FbException(ex.Message, ex); } } } #endregion #region InternalMethods internal void BeginTransaction() { lock (this) { try { IDatabase database = this.connection.InnerConnection.Database; this.transaction = database.BeginTransaction(this.BuildTpb()); } catch (IscException ex) { throw new FbException(ex.Message, ex); } } } internal void BeginTransaction(FbTransactionOptions options) { lock (this) { try { IDatabase database = this.connection.InnerConnection.Database; this.transaction = database.BeginTransaction(this.BuildTpb(options)); } catch (IscException ex) { throw new FbException(ex.Message, ex); } } } #endregion #region Private Methods private void UpdateTransaction() { if (this.connection != null) { this.connection.InnerConnection.TransactionUpdated(); } this.isUpdated = true; this.connection = null; this.transaction = null; } private TransactionParameterBuffer BuildTpb() { FbTransactionOptions options = FbTransactionOptions.Write; options |= FbTransactionOptions.Wait; /* Isolation level */ switch (this.isolationLevel) { case IsolationLevel.Serializable: options |= FbTransactionOptions.Consistency; break; case IsolationLevel.RepeatableRead: options |= FbTransactionOptions.Concurrency; break; case IsolationLevel.ReadUncommitted: options |= FbTransactionOptions.ReadCommitted; options |= FbTransactionOptions.RecVersion; break; case IsolationLevel.ReadCommitted: default: options |= FbTransactionOptions.ReadCommitted; options |= FbTransactionOptions.NoRecVersion; break; } return this.BuildTpb(options); } #if (!NETCF) private TransactionParameterBuffer BuildTpb(FbTransactionOptions options) { TransactionParameterBuffer tpb = new TransactionParameterBuffer(); tpb.Append(IscCodes.isc_tpb_version3); FbTransactionOptions[] o = (FbTransactionOptions[])Enum.GetValues(options.GetType()); for (int i = 0; i < o.Length; i++) { FbTransactionOptions option = ((FbTransactionOptions)(o[i])); if ((options & option) == option) { switch (option) { case FbTransactionOptions.Consistency: tpb.Append(IscCodes.isc_tpb_consistency); break; case FbTransactionOptions.Concurrency: tpb.Append(IscCodes.isc_tpb_concurrency); break; case FbTransactionOptions.Shared: tpb.Append(IscCodes.isc_tpb_shared); break; case FbTransactionOptions.Protected: tpb.Append(IscCodes.isc_tpb_protected); break; case FbTransactionOptions.Exclusive: tpb.Append(IscCodes.isc_tpb_exclusive); break; case FbTransactionOptions.Wait: tpb.Append(IscCodes.isc_tpb_wait); break; case FbTransactionOptions.NoWait: tpb.Append(IscCodes.isc_tpb_nowait); break; case FbTransactionOptions.Read: tpb.Append(IscCodes.isc_tpb_read); break; case FbTransactionOptions.Write: tpb.Append(IscCodes.isc_tpb_write); break; case FbTransactionOptions.LockRead: tpb.Append(IscCodes.isc_tpb_lock_read); break; case FbTransactionOptions.LockWrite: tpb.Append(IscCodes.isc_tpb_lock_write); break; case FbTransactionOptions.ReadCommitted: tpb.Append(IscCodes.isc_tpb_read_committed); break; case FbTransactionOptions.Autocommit: tpb.Append(IscCodes.isc_tpb_autocommit); break; case FbTransactionOptions.RecVersion: tpb.Append(IscCodes.isc_tpb_rec_version); break; case FbTransactionOptions.NoRecVersion: tpb.Append(IscCodes.isc_tpb_no_rec_version); break; case FbTransactionOptions.RestartRequests: tpb.Append(IscCodes.isc_tpb_restart_requests); break; case FbTransactionOptions.NoAutoUndo: tpb.Append(IscCodes.isc_tpb_no_auto_undo); break; } } } return tpb; } #else private TransactionParameterBuffer BuildTpb(FbTransactionOptions options) { TransactionParameterBuffer tpb = new TransactionParameterBuffer(); tpb.Append(IscCodes.isc_tpb_version3); if ((options & FbTransactionOptions.Consistency) == FbTransactionOptions.Consistency) { tpb.Append(IscCodes.isc_tpb_consistency); } if ((options & FbTransactionOptions.Concurrency) == FbTransactionOptions.Concurrency) { tpb.Append(IscCodes.isc_tpb_concurrency); } if ((options & FbTransactionOptions.Shared) == FbTransactionOptions.Shared) { tpb.Append(IscCodes.isc_tpb_shared); } if ((options & FbTransactionOptions.Protected) == FbTransactionOptions.Protected) { tpb.Append(IscCodes.isc_tpb_protected); } if ((options & FbTransactionOptions.Exclusive) == FbTransactionOptions.Exclusive) { tpb.Append(IscCodes.isc_tpb_exclusive); } if ((options & FbTransactionOptions.Wait) == FbTransactionOptions.Wait) { tpb.Append(IscCodes.isc_tpb_wait); } if ((options & FbTransactionOptions.NoWait) == FbTransactionOptions.NoWait) { tpb.Append(IscCodes.isc_tpb_nowait); } if ((options & FbTransactionOptions.Read) == FbTransactionOptions.Read) { tpb.Append(IscCodes.isc_tpb_read); } if ((options & FbTransactionOptions.Write) == FbTransactionOptions.Write) { tpb.Append(IscCodes.isc_tpb_write); } if ((options & FbTransactionOptions.LockRead) == FbTransactionOptions.LockRead) { tpb.Append(IscCodes.isc_tpb_lock_read); } if ((options & FbTransactionOptions.LockWrite) == FbTransactionOptions.LockWrite) { tpb.Append(IscCodes.isc_tpb_lock_write); } if ((options & FbTransactionOptions.ReadCommitted) == FbTransactionOptions.ReadCommitted) { tpb.Append(IscCodes.isc_tpb_read_committed); } if ((options & FbTransactionOptions.Autocommit) == FbTransactionOptions.Autocommit) { tpb.Append(IscCodes.isc_tpb_autocommit); } if ((options & FbTransactionOptions.RecVersion) == FbTransactionOptions.RecVersion) { tpb.Append(IscCodes.isc_tpb_rec_version); } if ((options & FbTransactionOptions.NoRecVersion) == FbTransactionOptions.NoRecVersion) { tpb.Append(IscCodes.isc_tpb_no_rec_version); } if ((options & FbTransactionOptions.RestartRequests) == FbTransactionOptions.RestartRequests) { tpb.Append(IscCodes.isc_tpb_restart_requests); } if ((options & FbTransactionOptions.NoAutoUndo) == FbTransactionOptions.NoAutoUndo) { tpb.Append(IscCodes.isc_tpb_no_auto_undo); } return tpb; } #endif #endregion } }