Как отловить SqlException из-за тупика? - PullRequest
85 голосов
/ 13 февраля 2010

Из приложения .NET 3.5 / C # я хотел бы перехватить SqlException, но , только если это вызвано взаимоблокировкой в экземпляре SQL Server 2008.

Типичное сообщение об ошибке: Transaction (Process ID 58) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

Тем не менее, это не похоже на документированную код ошибки для этого исключения.

Фильтрация исключений по наличию ключевого слова deadlock в их сообщениях кажется очень уродливым способом достижения такого поведения. Кто-нибудь знает правильный способ сделать это?

Ответы [ 3 ]

142 голосов
/ 13 февраля 2010

Код ошибки Microsft SQL Server для взаимоблокировки - 1205, поэтому вам нужно обработать исключение SqlException и проверить его. Так, например, если для всех других типов SqlException вы хотите, чтобы всплыло исключение:

catch (SqlException ex)
{
    if (ex.Number == 1205)
    {
        // Deadlock 
    }
    else
        throw;
}

Или, используя фильтрацию исключений, доступную в C # 6

catch (SqlException ex) when (ex.Number == 1205)
{
    // Deadlock 
}

Чтобы найти фактический код ошибки SQL для данного сообщения, удобно заглянуть в файл sys.messages в SQL Server.

, например

SELECT * FROM sys.messages WHERE text LIKE '%deadlock%' AND language_id=1033

Альтернативный способ обработки взаимоблокировок (от SQL Server 2005 и выше) заключается в том, чтобы сделать это в хранимой процедуре с использованием поддержки TRY ... CATCH:

BEGIN TRY
    -- some sql statements
END TRY
BEGIN CATCH
    IF (ERROR_NUMBER() = 1205)
        -- is a deadlock
    ELSE
        -- is not a deadlock
END CATCH

Вот полный пример здесь в MSDN о том, как реализовать логику повторных попыток взаимоблокировки исключительно в SQL.

40 голосов
/ 13 февраля 2010

Поскольку я предполагаю, что вы, возможно, захотите обнаружить взаимоблокировки, чтобы повторить неудачную операцию, я хотел бы предупредить вас о небольшом затруднении. Надеюсь, вы извините меня за то, что я немного не в теме.

Обнаруженная тупиком блокировка базы данных будет эффективно откатывать транзакцию, в которой вы выполняли (если есть), пока соединение остается открытым в .NET. Повторная попытка этой операции (в том же соединении) означает, что она будет выполнена в контексте без транзакций, и это может привести к повреждению данных.

Важно знать об этом. Лучше всего считать полное соединение обреченным на случай сбоя, вызванного SQL. Повторная попытка операции может быть выполнена только на уровне, где определена транзакция (путем повторного создания этой транзакции и ее соединения).

Поэтому, когда вы повторяете неудачную операцию, убедитесь, что вы открыли совершенно новое соединение и начали новую транзакцию.

2 голосов
/ 22 марта 2017

Вот способ обнаружения тупиковых ситуаций в C # 6.

try
{
    //todo: Execute SQL. 
    //IMPORTANT, if you used Connection.BeginTransaction(), this try..catch must surround that code. You must rollback the original transaction, then recreate it and re-run all the code.
}
catch (SqlException ex) when (ex.Number == 1205)
{
    //todo: Retry SQL
}

Убедитесь, что этот try..catch окружает всю вашу транзакцию.Согласно @Steven (подробности см. В его ответе), когда команда sql завершается неудачно из-за тупика, это вызывает откат транзакции и, если вы не создадите заново транзакцию, ваша повторная попытка будет выполнена вне контекстатранзакция и может привести к несоответствиям данных.

...