Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Microsoft. Data.SqlClient meldet Server- und Providerausfälle über SqlException. Eine Datenbankoperation kann mehrere Fehler zurückgeben, daher sollten Sie die Errors Sammlung der Ausnahme inspizieren, anstatt sich nur auf die kombinierte Messagezu verlassen.
Warnungen und Informationsnachrichten von der Datenbank-Engine werden über InfoMessageübermittelt. Abonnieren Sie das Thema, bevor Sie die Verbindung öffnen oder den Befehl ausführen, der die Nachricht erzeugen kann.
Behandle einen Datenbankfehler in folgender Reihenfolge:
- Fangen Sie
SqlExceptionan einer Begrenzung ab, die den Vorgangskontext hinzufügen oder eine Wiederherstellungsaktion auswählen kann. - Inspizieren Sie alle
SqlErrorin derErrorsSammlung. - Speichern Sie strukturierte Diagnosefelder, ohne Zugangsdaten oder sensible Werte aufzuzeichnen.
- Überprüfe den Verbindungszustand nach einem verbindungsbedingten Ausfall.
- Versuchen Sie es nur erneut, wenn der Fehler vorübergehend ist und die Operation sicher wiederholt werden kann.
Die Beispiele in diesem Artikel gehen davon aus, dass eine offene SqlConnection namens connection die AdventureWorks-Beispieldatenbank anspricht. Die C#-Beispiele erfordern using Microsoft.Data.SqlClient;.
Informationsnachrichten empfangen
Das InfoMessage Ereignis erhält Servernachrichten mit geringem Schweregrad, einschließlich der Ausgabe von PRINT. Ein Ereignis kann mehrere SqlError Objekte enthalten. Abonnieren, bevor Sie den Befehl ausführen, der eine Nachricht erzeugen kann:
connection.InfoMessage += (_, eventArgs) =>
{
foreach (SqlError message in eventArgs.Errors)
{
Console.WriteLine(
$"Info {message.Number}, class {message.Class}: {message.Message}");
}
};
using var command = new SqlCommand(
"PRINT N'Connected and ready.';",
connection);
command.ExecuteNonQuery();
Der Auszug lautet:
Info 0, class 0: Connected and ready.
Catch SqlException
Fangen Sie SqlException um die Datenbankoperation ab, die Sie behandeln oder mit zusätzlichen Informationen anreichern können. Verwenden Sie keinen allgemeinen Catch-Block, um eine fehlgeschlagene Datenbankoperation in ein erfolgreiches Ergebnis zu verwandeln.
Eine Datenbankoperation kann mehr als einen Fehler zurückgeben. Jede SqlError in Errors umfasst strukturierte Felder:
| Property | Meaning |
|---|---|
Number |
Datenbank-Engine oder Anbieter-Fehlernummer. |
Class |
Schweregrad. |
State |
Serverdefinierter Zustand, der Positionen für dieselbe Fehlernummer unterscheiden kann. |
LineNumber |
Gemeldete Zeilennummer. |
Procedure |
Verfahren oder Auslösername, falls angegeben. |
Server |
Für den Fehler gemeldeter Servername. |
Message |
Menschenlesbarer Fehlertext. |
Die Komforteigenschaften auf SqlException, einschließlich Number, Class, , State, LineNumber, Procedure, und Server, spiegeln nur den ersten Fehler wider. Der folgende Auszug gibt eine Meldung aus und löst dann im selben Batch einen Fehler aus. Es liest exception.Number und iteriert Errors dann, um jeden Eintrag zu melden:
using (var failing = new SqlCommand("""
PRINT N'Starting controlled operation.';
RAISERROR (N'The controlled operation failed.', 16, 1);
""", connection))
{
try
{
await failing.ExecuteNonQueryAsync();
}
catch (SqlException exception)
{
Console.WriteLine($"First error number: {exception.Number}");
foreach (SqlError error in exception.Errors)
{
Console.WriteLine(
$"Number={error.Number}; Class={error.Class}; " +
$"State={error.State}; Message={error.Message}");
}
}
}
Der Auszug lautet:
First error number: 50000
Number=50000; Class=16; State=1; Message=The controlled operation failed.
Number=0; Class=0; State=1; Message=Starting controlled operation.
Der Ausgang PRINT erhöht InfoMessage nicht. Wenn ein Batch einen Fehler zurückgibt, der SqlException auslöst, fügt der Anbieter die Informationsnachrichten dieses Batches am Ende von SqlException.Errors an.
Dieser Auszug fängt den kontrollierten Fehler nur ab, um seine Felder anzuzeigen. Im Anwendungscode wird mit throw; erneut ausgelöst, es sei denn, der Catch-Block ergreift eine tatsächliche Wiederherstellungsmaßnahme. Triff Entscheidungen anhand dokumentierter Fehlernummern und des Betriebskontexts. Parsen Sie nicht den lokalisierten Nachrichtentext.
Überprüfen Sie den Verbindungsstatus
Die Datenbank-Engine weist jeder Nachricht eine Schweregradsklasse zu. Basisklassen sind informativ. Klassen, die als Fehler behandelt werden, erzeugen SqlException. Fehler mit hoher Schwere können die Verbindung geschlossen oder defekt hinterlassen.
Nach einem Fehler prüfen State Sie, bevor Sie die Verbindung wiederverwenden:
using var command = new SqlCommand(
"RAISERROR (N'Controlled failure.', 16, 1);",
connection);
try
{
command.ExecuteNonQuery();
}
catch (SqlException)
{
Console.WriteLine($"Connection state: {connection.State}");
}
Gehen Sie nicht davon aus, dass eine Verbindung nutzbar bleibt, nur weil die Ausnahme abgefangen wurde. Ein Fehler von Schweregrad 16 lässt sie normalerweise offen, aber verbindungsunterbrechungen können sie schließen. Geben Sie eine geschlossene oder unterbrochene Verbindung frei und holen Sie sich eine weitere Verbindung aus dem Pool. Kodiere die Wiederholungsrichtlinien nicht allein anhand des Schweregrads. Die Operation muss auch sicher wiederholt werden können.
Identifizieren Sie einen fehlgeschlagenen Batch-Befehl
Wenn ein SqlBatch fehlschlägt, SqlException.BatchCommand kann auf das zugehörige SqlBatchCommandverweisen. Die Eigenschaft ist null, wenn der Anbieter den Fehler nicht einem Batch-Befehl zuordnen kann. Der folgende Auszug führt zwei Batch-Befehle aus. Die zweite schlägt fehl:
using (var batch = new SqlBatch(connection))
{
batch.BatchCommands.Add(new SqlBatchCommand("SELECT 1;"));
batch.BatchCommands.Add(new SqlBatchCommand("SELECT 1 / 0;"));
try
{
await batch.ExecuteNonQueryAsync();
}
catch (SqlException exception)
{
Console.WriteLine($"Error {exception.Number}: {exception.Message}");
if (exception.BatchCommand is SqlBatchCommand failedCommand)
{
Console.WriteLine($"Failed command: {failedCommand.CommandText}");
}
}
}
Der Auszug lautet:
Error 8134: Divide by zero error encountered.
Failed command: SELECT 1 / 0;
Befehlstext kann in schlecht parametrisierten Anwendungen sensitive Literale enthalten. Bevorzuge stabile Anwendungsoperationsnamen in Produktionsprotokollen und protokolliere standardmäßig keine Parameterwerte.
Leite Benutzerfehler an InfoMessage weiter
Der Standardwert von FireInfoMessageEventOnUserErrors ist false. Wenn Sie es auf true setzen, gehen Fehler mit Schweregrad 16 oder niedriger an den InfoMessage-Handler, anstatt SqlException auszulösen:
connection.InfoMessage += (_, eventArgs) =>
{
foreach (SqlError message in eventArgs.Errors)
{
Console.WriteLine(
$"Info {message.Number}, class {message.Class}: {message.Message}");
}
};
connection.FireInfoMessageEventOnUserErrors = true;
using var command = new SqlCommand(
"RAISERROR (N'Routed to InfoMessage.', 16, 1);",
connection);
command.ExecuteNonQuery();
Es wird keine Ausnahme ausgelöst. Der Auszug lautet:
Info 50000, class 16: Routed to InfoMessage.
Verwenden Sie diese Option nur, wenn die Anwendung ein bewusst vorgesehenes ereignisbasiertes Fehlermodell hat, da der aufrufende Code keine Ausnahme mehr sieht. Fehler mit Schweregrad von 17 oder höher lösen SqlExceptionweiterhin aus. Setze die Eigenschaft einmal als Teil der Verbindungskonfiguration, anstatt sie bei einzelnen Befehlen umzuschalten.
Diagnosekontext protokollieren
Erfassen Sie genügend strukturierten Kontext, um den Fehler nachzuvollziehen, ohne Daten freizulegen. Der folgende Auszug protokolliert den Namen eines Anwendungsvorgangs, die ClientConnectionId, und die Felder jedes SqlError.
ClientConnectionId Hilft, einen Clientausfall mit Server- und Netzwerkdiagnostik in Verbindung zu bringen. Es ist keine Qualifikation.
const string operation = "LoadOrders";
using (var load = new SqlCommand("SELECT 1 / 0;", connection))
{
try
{
await load.ExecuteScalarAsync();
}
catch (SqlException exception)
{
Console.WriteLine(
$"Operation={operation}; " +
$"ClientConnectionId={exception.ClientConnectionId}");
foreach (SqlError error in exception.Errors)
{
Console.WriteLine(
$"Number={error.Number}; Class={error.Class}; " +
$"State={error.State}; Procedure={error.Procedure}; " +
$"Line={error.LineNumber}");
}
}
}
Der Wert ClientConnectionId variiert bei jeder Verbindung. Der Ausschnitt gibt eine ähnliche Ausgabe aus:
Operation=LoadOrders; ClientConnectionId=<connection ID>
Number=8134; Class=16; State=1; Procedure=; Line=1
Zeichnen Sie außerdem auf, ob eine Transaktion aktiv war und, falls BatchCommand verfügbar, welcher Batch-Befehl fehlgeschlagen ist. Fügen Sie bereinigte Server- und Datenbankidentifikatoren nur dann ein, wenn die organisatorische Richtlinie dies erlaubt. Protokolliere keine Verbindungsstrings, Zugriffstoken, Passwörter oder sensible Parameterwerte. Verwenden Sie Diagnosezähler, Ereignisquellenverfolgung und den SqlClient-Fehlerbehebungsleitfaden für eine tiefere Untersuchung.
Verwandte Inhalte
- Kehren Sie zum Befehlsausführungs-Workflow zurück.
- Um einen fehlgeschlagenen Befehl in einer Gruppe zu identifizieren, führen Sie mehrere Befehle mit SqlBatch aus.
- Um beschränkte Wiederholungen hinzuzufügen, verwenden Sie konfigurierbare Wiederholungslogik.
- Wenn Ausnahmefelder nicht ausreichen, nutze die Fehlerbehebungsanleitung von SqlClient.