Як зловити SqlException, викликаний тупиком?


92

З програми .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.

Проте, схоже, це не задокументований код помилки для цього винятку.

Фільтрування винятків щодо наявності ключового слова глухого кута в їх повідомленні видається дуже потворним способом досягнення такої поведінки. Хтось знає правильний спосіб зробити це?


3
Я (нарешті) знайшов документацію до коду помилки: msdn.microsoft.com/en-us/library/aa337376.aspx . Ви також можете знайти це через сам SQL Server:select * from master.dbo.sysmessages where error=1205
Мартін Макнулті

Відповіді:


154

Спеціальний код помилки 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.


2
Зверніть увагу, що коди помилок специфічні для постачальника, тому 1205 є глухим кутом для SQL Server, але він може відрізнятися для Oracle, MySQL тощо
brianmearns

3
Залежно від рівня даних, файл SqlExceptionможе бути перенесений в інший. Тож нам може знадобитися зловити будь-який вид винятків і перевірити їх, а потім, якщо вони не є безпосередньо тупиковим винятком, рекурсивно перевірити їх InnerException.
Фредерік,

46

Оскільки я припускаю, що ви, можливо, хочете виявити тупикові ситуації, щоб мати можливість повторити невдалу операцію, я хотів би попередити вас про те, щоб трохи зачепити. Сподіваюсь, ви вибачите мене за те, що я тут трохи не в темі.

Тупиковий спосіб, виявлений базою даних, ефективно відмовить транзакцію, в якій ви працювали (якщо вона була), а з’єднання залишається відкритим у .NET. Повторна спроба цієї операції (у тому самому підключенні) означає, що вона буде виконана в контексті без транзакцій, і це може призвести до пошкодження даних.

Важливо це усвідомлювати. Краще вважати повне з’єднання приреченим на випадок збою, спричиненого SQL. Повторна спроба операції може бути здійснена лише на рівні, де визначена транзакція (шляхом відтворення цієї транзакції та її з'єднання).

Тому, коли ви повторюєте невдалу операцію, переконайтеся, що ви відкрили абсолютно нове з’єднання та розпочали нову транзакцію.


4
Навіщо потрібне абсолютно нове з’єднання? Я відправив питання про цій відповіді тут .
Сем

3

Ось 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
}

Переконайтеся, що ця спроба..влов оточує всю вашу транзакцію. Відповідно до @Steven (докладніше див. Його відповідь), коли команда sql не вдається через глухий кут, це призводить до відкочування транзакції, і якщо ви не відтворете транзакцію, ваша повторна спроба буде виконана поза контекстом транзакції та може призвести до невідповідності даних.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.