2010-02-13 7 views
79

Von einer .NET 3.5/C# -App möchte ich SqlException aber nur fangen, wenn es von Deadlocks auf einer SQL Server 2008-Instanz verursacht wird.Wie fangen SqlException verursacht durch Deadlock?

Typische Fehlermeldung ist Transaction (Process ID 58) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

Doch es ist kein dokumentiert error code für diese Ausnahme zu sein scheint.

Filterung Ausnahme gegen das Vorhandensein der Deadlock Schlüsselwort in ihrer Nachricht scheint eine sehr hässliche Möglichkeit, dieses Verhalten zu erreichen. Kennt jemand den richtigen Weg, dies zu tun?

+2

Ich (endlich) fand die Dokumentation für den Fehlercode: http://msdn.microsoft.com/en-us/library/aa337376.aspx. Sie können dies auch über SQL Server selbst finden: 'Wählen Sie * aus master.dbo.sysmessages wobei error = 1205' –

Antwort

127

Der Microsft SQL Server-spezifische Fehlercode für einen Deadlock ist 1205, also müssen Sie die SqlException behandeln und überprüfen Sie dies. So z.B. wenn für alle anderen Arten von SqlException Sie die Blase wollen die Ausnahme auf:

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

Oder Ausnahme Filterung in C# 6

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

Eine praktische Sache zu tun, die eigentliche SQL-Fehlercode zu finden Suchen Sie in einer gegebenen Nachricht in sys.messages in SQL Server.

z.B.

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

Eine alternative Möglichkeit, Deadlocks zu handhaben (von SQL Server 2005 und höher), ist es innerhalb einer gespeicherten Prozedur zu tun, um die versuchen, mit ... CATCH-Unterstützung:

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

Es gibt ein vollständiges Beispiel here in MSDN wie Deadlock Retry Logik rein innerhalb SQL zu implementieren.

+2

Beachten Sie, dass die Fehlercodes herstellerspezifisch sind, so dass 1205 ein Deadlock für SQL Server ist, aber für Oracle möglicherweise anders sein kann. MySQL usw. – brianmearns

+3

Abhängig von der Datenschicht kann die 'SqlException' in eine andere eingeschlossen werden. Daher müssen wir möglicherweise jede Ausnahmeart abfangen und sie dann überprüfen, wenn sie nicht direkt eine Deadlock-Ausnahme sind, ihre "InnerException" rekursiv überprüfen. –

36

Weil ich vermute, dass Sie möglicherweise Deadlocks erkennen möchten, um die fehlgeschlagene Operation wiederholen zu können, warne ich Sie gerne für ein kleines Gotcha. Ich hoffe, Sie werden mich dafür entschuldigen, dass ich hier ein bisschen abseits bin.

Ein Deadlock, der von der Datenbank erkannt wird, führt zu einem Rollback der Transaktion, in der Sie ausgeführt wurden (sofern vorhanden), während die Verbindung in .NET geöffnet bleibt. Das Wiederholen dieser Operation (in derselben Verbindung) bedeutet, dass sie in einem transaktionslosen Kontext ausgeführt wird und dies zu Datenkorruption führen kann.

Es ist wichtig, sich dessen bewusst zu sein. Es ist am besten, die vollständige Verbindung zu betrachten, die im Falle eines Fehlers, der durch SQL verursacht wird, zum Scheitern verurteilt ist. Das Wiederholen der Operation kann nur auf der Ebene durchgeführt werden, auf der die Transaktion definiert ist (durch erneutes Erstellen dieser Transaktion und ihrer Verbindung).

Wenn Sie einen fehlgeschlagenen Vorgang erneut versuchen, stellen Sie sicher, dass Sie eine vollständig neue Verbindung öffnen und eine neue Transaktion starten.

+4

Warum brauchen Sie eine komplett neue Verbindung? Ich habe eine Frage zu dieser Antwort [hier] (http://stackoverflow.com/q/19108680/238753) gestellt. – Sam

2

Hier ist eine C# 6-Methode zur Erkennung von Deadlocks.

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 
} 

Stellen Sie sicher, diese try..catch Ihre gesamte Transaktion umgibt. Laut @Steven (siehe seine Antwort für Details) führt der sql-Befehl aufgrund des Deadlocks zu einem Rollback der Transaktion, und wenn Sie die Transaktion nicht erneut erstellen, wird Ihre Wiederholung außerhalb des Kontexts von ausgeführt die Transaktion und kann zu Dateninkonsistenzen führen.

Verwandte Themen