2010-02-13 16 views
79

Z aplikacji .NET 3.5/C# chciałbym złapać SqlException, ale tylko wtedy, gdy jest spowodowane przez zakleszczenia na wystąpienie programu SQL Server 2008.Jak złapać wyjątek SqlException spowodowany impasem?

Typowy komunikat o błędzie jest Transaction (Process ID 58) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

Jednak nie wydaje się być udokumentowane error code dla tego wyjątku.

Wyjątek filtrowania przed wystąpieniem impasu w ich komunikacie wydaje się bardzo brzydkim sposobem na osiągnięcie tego zachowania. Czy ktoś wie, jak to zrobić?

+2

I (w końcu) znalazłem dokumentację kodu błędu: http://msdn.microsoft.com/en-us/library/aa337376.aspx. Możesz to również znaleźć w samym Serwerze SQL: 'select * from master.dbo.sysmessages gdzie error = 1205' –

Odpowiedz

127

Kod błędu specyficzny dla serwera Microsoft Microsft dla zakleszczenia wynosi 1205, więc musisz obsłużyć wyjątek SqlException i sprawdzić, czy nie. Tak więc np. jeśli na wszystkie inne rodzaje SQLException chcesz bańki wyjątek up:

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

Albo, stosując filtrowanie wyjątku dostępne w języku C# 6

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

Poręczny zrobić, aby znaleźć rzeczywiste kod błędu SQL dla danej wiadomości, należy szukać w sys.messages w SQL Server.

np.

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

Alternatywnym sposobem obsługi zakleszczenia (z SQL Server 2005 i powyżej), jest to zrobić w ramach procedury przechowywanej za pomocą TRY ... CATCH wsparcie:

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

Jest pełna przykładów here w MSDN, jak zaimplementować logikę impulsu logicznego czysto w SQL.

+2

Zauważ, że kody błędów są specyficzne dla dostawcy, więc 1205 jest impasem dla SQL Server, ale może być inne dla Oracle, MySQL itp. – brianmearns

+3

W zależności od warstwy danych, 'SqlException' może być zawinięty w inny. Więc możemy potrzebować potrzeby wychwycenia dowolnego rodzaju wyjątku i sprawdzić je, jeśli nie są one bezpośrednio wyjątkiem zakleszczenia, rekurencyjnie sprawdź ich wyjątek "InnerException". –

36

Ponieważ prawdopodobnie chcesz wykryć zakleszczenia, aby móc ponowić nieudaną operację, chciałbym ostrzec cię o małej gafie. Mam nadzieję, że wybaczycie mi, że jestem tutaj nieco nie na temat.

Zakleszczenie wykryte przez bazę danych skutecznie wycofa transakcję, w której działałeś (jeśli jest), podczas gdy połączenie pozostaje otwarte w .NET. Ponowna próba wykonania tej operacji (w tym samym połączeniu) oznacza, że ​​zostanie wykonana w kontekście bez transakcji, co może doprowadzić do uszkodzenia danych.

Ważne jest, aby zdawać sobie z tego sprawę. Najlepiej jest rozważyć całkowite połączenie skazane na niepowodzenie spowodowane przez SQL. Ponowna operacja może zostać wykonana tylko na poziomie, na którym zdefiniowana jest transakcja (poprzez odtworzenie tej transakcji i jej połączenia).

Tak więc, gdy ponawiasz operację, która się nie powiodła, upewnij się, że otwierasz zupełnie nowe połączenie i rozpoczynasz nową transakcję.

+4

Dlaczego potrzebujesz zupełnie nowego połączenia? Wysłałem pytanie dotyczące tej odpowiedzi [tutaj] (http://stackoverflow.com/q/19108680/238753). – Sam

2

Oto sposób wykrywania zakleszczenia w 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 
} 

Upewnij się, że to try..catch otacza całą transakcję. Według @Steven (zobacz jego odpowiedź dla szczegółów), gdy polecenie sql nie powiedzie się z powodu zakleszczenia, powoduje ono wycofanie transakcji, a jeśli nie odtwarzasz transakcji, twoja ponowna próba zostanie wykonana poza kontekstem transakcji i może spowodować niespójności danych.

Powiązane problemy