/// <summary> /// Begin transaction /// </summary> /// <param name="parentTransaction">Parent Transaction that begin this transaction</param> /// <returns>DataAccessTransactionDb transaction object</returns> public DataAccessTransactionDb BeginTransaction(Transaction parentTransaction) { CheckState(); var transaction = new DataAccessTransactionDb(this, parentTransaction); transactions.Push(transaction); return(transaction); }
/// <summary> /// Commit transaction /// </summary> /// <param name="transaction">DataAccessTransactionDb transaction object for commit</param> internal void CommitTransaction(DataAccessTransactionDb transaction) { if (transactions.Count == 0) { throw new TransactionException("There is no started transactions"); } if (transactions.First() != transaction) { throw new TransactionException("This is not last started transaction"); } if (transactions.Count == 1) { // This is last DataAccess Transaction. Commit real db transaction, if it`s exists if (dbTransaction != null) { // Уведомляем основное соединение о начале физического коммита if (transaction.ParentTransaction != null) { transaction.ParentTransaction.Connection.OnPhysicalCommit(); } var tmpTransaction = dbTransaction; var tmpConnection = dbTransaction.Connection; dbTransaction = null; try { tmpTransaction.Commit(); tmpConnection.Close(); } catch (Exception ex) { // Принудительное закрытие соединения без проброса ошибки // использовать просто using для вызова Dispose нельзя так как если будет ошибка // при вызове Dispose в using то наш Exception не дойдет // https://msdn.microsoft.com/ru-ru/library/aa355056%28v=vs.110%29.aspx try { tmpConnection.Dispose(); } catch { } throw new TransactionUnknowStateException(ex.Message, ex); } } } transactions.Pop(); }
/// <summary> /// Create command object /// </summary> /// <param name="transaction">Transaction object</param> /// <param name="sql">Sql query</param> /// <param name="parameters">List of Parameters. Can used specific parameters also as SqlParameter, etc</param> /// <param name="isStoredProcedure">True if sql is stored procedure</param> /// <returns>DbCommand object</returns> private DbCommand CreateCommand(DataAccessTransactionDb transaction, string sql, IList <DbParameter> parameters, bool isStoredProcedure) { DbCommand dbCommand = dbProviderFactory.CreateCommand(); dbCommand.CommandText = sql; dbCommand.Connection = transaction.DbTransaction.Connection; dbCommand.Transaction = transaction.DbTransaction; if (isStoredProcedure) { dbCommand.CommandType = CommandType.StoredProcedure; } if (parameters != null) { foreach (var parameter in parameters) { dbCommand.Parameters.Add(parameter); } } return(dbCommand); }
/// <summary> /// Rollback transaction /// </summary> /// <param name="transaction">DataAccessTransactionDb transaction object for rollback</param> internal void RollbackTransactions(DataAccessTransactionDb transaction) { // Сохраняем первое исключение Exception firstException = null; // Rollback real db transaction if (dbTransaction != null) { var tmpTransaction = dbTransaction; var tmpConnection = dbTransaction.Connection; dbTransaction = null; try { if (tmpTransaction != null) { tmpTransaction.Rollback(); } if (tmpConnection != null) { tmpConnection.Close(); } } catch (Exception ex) { if (firstException == null) { firstException = ex; } // Принудительное закрытие соединения без проброса ошибки // использовать просто using для вызова Dispose нельзя так как если будет ошибка // при вызове Dispose в using то наш Exception не дойдет // https://msdn.microsoft.com/ru-ru/library/aa355056%28v=vs.110%29.aspx try { tmpConnection.Dispose(); } catch { } } } // Rollback all runned transactions if (TransactionsCount > 0) { var transactionsToRollback = transactions; transactions = new Stack <DataAccessTransactionDb>(); foreach (var item in transactionsToRollback) { try { item.Rollback(); if (item.ParentTransaction != null) { item.ParentTransaction.Rollback(); } } catch (Exception ex) { if (firstException == null) { firstException = ex; } } } } if (firstException != null) { throw new TransactionUnknowStateException(firstException.Message, firstException); } }