/// <summary> /// Выполняет действие с экземпляром SqlCommand, созданным по подключению соответсвующему данному адаптеру. /// </summary> /// <param name="query">Текст команды.</param> /// <param name="executeCommandCode">Действие выполняемой с экземпляром SqlCommand.</param> /// <param name="sqlParameters">Параметры команды.</param> private void ExecuteCommandInternal(string query, ExecuteCommandCode executeCommandCode, params SqlParameter[] sqlParameters) { if (string.IsNullOrEmpty(query)) { throw new ArgumentNullException("query"); } if (executeCommandCode == null) { throw new ArgumentNullException("executeCommandCode"); } //получаем текущую транзакцию из контекста. DBTransaction currentTransaction = DBTransaction.Current; //если sql-транзакция отсутствует, выполняем обычный запрос не в транзакции, используя общий коннект для всех вложенных комманд, если он имеется. //иначе - создаем открытый коннект, действующий в продолжение всей транзакции. if (currentTransaction == null || this.Connection.Context.IsSuppressContext) { //выполняем действие без Sql-транзакции. //создаем подключение, команду и выполняем запрос using (SqlConnection contextConnection = new SqlConnection(this.Connection.ConnectionString)) { contextConnection.Open(); //выполняем команду в рамках открытого подключения. this.TryExecuteCommand(0, query, contextConnection, null, executeCommandCode, sqlParameters); } } else { //выполняем действие в Sql-транзакции. //инициализируем транзакцию данным подключением. currentTransaction.InitTransaction(this.Connection); //выполняем команду, используя подключение транзакции. this.TryExecuteCommand(0, query, currentTransaction.OpenedConnection, currentTransaction.OpenedTransaction, executeCommandCode, sqlParameters); //проверяем, что коннект не закрыт, для возможности выполнения следующих комманд при помощи данного коннекта. //если даже код до сюда не дойдет, то это не вызовет утечку ресурсов, поэтому блок try/finaly можно здесь не ставить. currentTransaction.CheckConnectionOpened(); } }
private void TryExecuteCommand(int tryCount, string query, SqlConnection contextConnection, SqlTransaction contextTransaction, ExecuteCommandCode executeCommandCode, params SqlParameter[] sqlParameters) { if (string.IsNullOrEmpty(query)) { throw new ArgumentNullException("query"); } if (contextConnection == null) { throw new ArgumentNullException("contextConnection"); } if (executeCommandCode == null) { throw new ArgumentNullException("executeCommandCode"); } //инициализируем текст команды. string cmdText = query; //если задана контекстная транзакция всегда задаем контекст БД, //поскольку после выполнения запроса в экземпляре SqlConnection свойство Database изменяется и становится отличным от Initial Catalog. if (contextTransaction != null) { cmdText = string.Format(@"USE [{0}] {1}", this.Connection.DatabaseName, query); } try { using (SqlCommand cmd = new SqlCommand(cmdText, contextConnection)) { //если задана транзакция, назначаем ее. if (contextTransaction != null) { cmd.Transaction = contextTransaction; } this.SetCommandTimeout(cmd); if (sqlParameters != null) { foreach (SqlParameter parameter in sqlParameters) { if (parameter == null) { continue; } SqlParameter ensuredParam = this.EnsureParameter(tryCount, parameter); cmd.Parameters.Add(ensuredParam); } } //выполняем команду. executeCommandCode(cmd); } } catch (Exception ex) { string errorText = ex.ToString().ToLower(); //разрешаем перезапуск по взаимоблокировке только, если код выполняется вне транзакции. bool canRetry = (errorText.Contains("deadlock") || errorText.Contains("взаимоблокировк")) && contextTransaction == null; if (!canRetry || tryCount == MaxTryCounts - 1) { //формируем окончательную ошибку и отправляем её наверх. Exception completedEx = new Exception(string.Format("Ошибка при выполнении запроса: {0}Не удалось выполнить запрос в базе данных {1} ***** {2} *****.", this.GetErrorsStack(ex), this.Connection.DisplayName, cmdText), ex); //логируем ошибку, если не применяется логирование методом DBConnectionContext.ExecuteTransactionInternal. if (!this.Connection.Context.IsExecuteTransactionContext) { this.Connection.Context.WriteEventLog(completedEx); } //отправляем ошибку. throw completedEx; } else { //делаем обработку взаимоблокировок: засыпаем поток, и пытаемся выполнить запрос еще раз. Thread.Sleep(200); //если запрос выполняется в транзакции, то обработку взаимоблокировки делаем вне транзакции. using (SqlConnection suppressedConnection = new SqlConnection(this.Connection.ConnectionString)) { suppressedConnection.Open(); this.TryExecuteCommand(tryCount + 1, query, suppressedConnection, null, executeCommandCode, sqlParameters); } } } }
/// <summary> /// Выполняет действие с экземпляром SqlCommand, созданным по подключению соответсвующему данному адаптеру. /// </summary> /// <param name="query">Текст команды.</param> /// <param name="executeCommandCode">Действие выполняемой с экземпляром SqlCommand.</param> /// <param name="sqlParameters">Параметры команды.</param> public void ExecuteCommand(string query, ExecuteCommandCode executeCommandCode, params SqlParameter[] sqlParameters) { this.ExecuteCommandInternal(query, executeCommandCode, sqlParameters); }