2017-02-16 4 views
2

Я помещал set xact_abort on в операторы SQL-команд и заметил, что он не откатывает обновления, вставки и т. Д. В моем C# SqlCommand при ошибке. Взято с this сообщение. MSDN гласит:Ошибка SQL не откат всего SqlCommand

Когда SET XACT_ABORT включена, если Transact-SQL заявление вызывает ошибку во время выполнения, вся транзакция завершается, и откат.

Общий формат моего SQl запроса:

set xact_abort on 
INSERT INTO Table1 
UPDATE Table2 
UPDATE Table3 
UPDATE Table4 
--Finally 
SELECT someValue 

я заметил, что при ошибке моя команда SQL была не откат. Частная ошибка заключается в том, что длина данных одного параметра превышала указанную длину столбцов. Я использую SqlCommand и SqlParameter для создания SQL-запросов.

Я не ищу обработку исключений в SQL, но очень важно, чтобы любые ошибки не вносили никаких изменений в базу данных.

Обычно ошибки включают в себя: столбец не существует, неправильный тип данных, данные будут усечены из-за длины и т.д.

Должен ли я использовать что-то другое, чем set xact_abort on? Заранее спасибо!

+0

Почему бы не поймать 'SqlException' на вашем C# и откатить транзакцию вручную? – Andrew

+4

В нижней части [этой ссылки] (https://msdn.microsoft.com/en-us/library/ms188792.aspx) вам все равно нужно обернуть инструкции в вызовы 'BeginTransaction' и' EndTransaction'. Похоже, это просто избавляет вас от необходимости отвечать на ошибки и вызывать «Rollback» самостоятельно. – DonBoitnott

+0

Если вы работаете с несколькими сценариями, другим способом управлять им будет [dbup] (http://dbup.github.io). – lloyd

ответ

3

Заявление вы процитировали из MSDN правильно: с XACT_ABORT набором для ON, любой ошибка будет прервать партию и откат любой активной транзакции. Путаница здесь заключается в том, что в SQL Server каждый оператор по умолчанию является транзакцией. Если вы хотите сгруппировать несколько операторов в явную транзакцию, вам необходимо использовать BEGIN TRAN; вместе с COMMIT;. Следующий пример иллюстрирует это поведение:

Выполнить это:

SET XACT_ABORT ON; 
CREATE TABLE #Bob (ID INT); 

INSERT INTO #Bob (ID) VALUES (1); 

BEGIN TRAN; 

INSERT INTO #Bob (ID) VALUES (2); 
INSERT INTO #Bob (ID) VALUES (3); 
INSERT INTO #Bob (ID) VALUES (4/0); 

COMMIT TRAN; 

Затем запустите это отдельно (так как ошибка в формулировках выше прервет всю партию - за счет использования XACT_ABORT ON - и так в SELECT никогда не будет выполняться, если вы попытаетесь запустить SELECT одновременно):

SELECT * FROM #Bob; 

Она возвращает одну строку, содержащую 1 с этого выполненного оператора само по себе, а не остроумием hin явная транзакция. Когда вы добавите в свой код BEGIN TRAN; и COMMIT TRAN;, он будет работать так, как вы ожидаете.

+0

Спасибо! Даже не знал о тонкости, о которой вы говорили, и не использовал транзакции в моих SQL-запросах на сегодняшний день. Я также изучил некоторые из ошибок обработки транзакций SQL, которые могли бы помочь проверить и устранить несколько запросов операторов. – Matt

1

Один образец для решения сделок с C# код выглядит следующим образом:

try 
{ 
    var connection = new SqlConnection(connectionString); 
    connection.Open(); 

    var trans = connection.BeginTransaction(); 

    using (var command = connection .CreateCommand()) 
    { 
     command.Transaction = trans; 
     command.CommandText = "..."; 
     command.ExecuteNonQuery(); 
    } 

    // other commands may be defined here 
    // command can be included or excluded from transaction (do not set Transaction property) 

    // commits the transaction 
    trans.Commit(); 
} 
// best practice is to catch specific exception types like `SqlException` 
catch (Exception ex) //error occurred 
{ 
    trans.Rollback(); 
    // log error somewhere 
} 
finally 
{ 
    // execute no-matter what 
} 

Этот шаблон имеет следующие преимущества:

  1. Нет необходимости беспокоиться о SET XACT_ABORT (OFF по умолчанию)
  2. Улучшенная обработка исключений

Примечание: вас может заинтересовать Unit of Work pattern

+0

Спасибо, даже не подозревал о транзакциях SqlCommand! Несмотря на то, что это не может быть решением моего первоначального вопроса, это может быть лучший подход, поскольку я уже использую SqlCommands. – Matt

+0

Это ответ на ваш _problem_, а не на ваш _question_. Легче понять, что не удалось (управление исключениями, протоколирование), если вы управляете транзакцией из кода C#. Конечно, если вы настаиваете на написании всего в SQL, принятый ответ будет прекрасен. Однако лучше всего использовать блок 'try .. catch' в SQL, как указано [здесь] (http://stackoverflow.com/a/25147029/2780791). – Alexei

Смежные вопросы