Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
A Microsoft.Data.SqlClient a(z) SqlException segítségével jelenti a szerver- és szolgáltatóhibákat. Egy adatbázis-művelet több hibát is visszaadhat, ezért vizsgáld meg a kivétel Errors gyűjteményét ahelyett, hogy csak az egyesített Message elemre hagyatkoznál.
Az adatbázismotorból érkező figyelmeztetések és információs üzenetek a InfoMessage útján érkeznek. Iratkozz fel, mielőtt megnyitod a csatlakozást vagy végrehajtod azt a parancsot, amely előidézheti az üzenetet.
Kezelje az adatbázis hibáját a következő sorrendben:
- Kapd el a(z)
SqlExceptionelemet egy hibahatáron belül, amely műveleti környezetet adhat hozzá, vagy válassz egy helyreállítási műveletet. - Vizsgálja meg az összes
SqlErrorelemet a(z)Errorsgyűjteményben. - Rögzítsen strukturált diagnosztikai mezőket anélkül, hogy rögzítené a hitelesítő adatokat vagy érzékeny értékeket.
- Ellenőrizd a kapcsolati állapotot egy kapcsolódással kapcsolatos hiba után.
- Csak akkor próbálja újra, ha a hiba átmeneti, és a művelet biztonságosan megismételhető.
A cikkben szereplő példák feltételezik, hogy egy nyitott SqlConnection, amelynek neve connection, a(z) AdventureWorks mintaadatbázist célozza meg. A C#-példák megkövetelik using Microsoft.Data.SqlClient;.
Információs üzenetek fogadása
Az InfoMessage esemény alacsony súlyosságú szerverüzeneteket kap, beleértve a PRINT-ből származó kimenetet is. Egy esemény több SqlError objektumot is tartalmazhat. Iratkozz fel, mielőtt végrehajtanád azt a parancsot, amely üzenetet generálhat:
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();
A részlet így szól:
Info 0, class 0: Connected and ready.
Catch SqlException
Tegyél SqlException az adatbázis-művelet köré, amelyet kezelni vagy további információval kiegészíteni tudsz. Ne használj általános catch blokkot arra, hogy egy sikertelen adatbázis-műveletet sikeres eredménnyé alakíts.
Egy adatbázis-művelet több hibát is visszaadhat. A(z) Errors minden SqlError eleme strukturált mezőket tartalmaz:
| Property | Meaning |
|---|---|
Number |
Adatbázis motor vagy szolgáltató hibaszáma. |
Class |
Súlyossága. |
State |
Szerver által definiált állapot, amely meg tudja különböztetni az ugyanazon hibaszámhoz tartozó helyeket. |
LineNumber |
Jelentett sorszám. |
Procedure |
Eljárás vagy trigger neve, ha meg van adva. |
Server |
A hibához megadott szervernév. |
Message |
Ember által olvasható hibaszöveg. |
A kényelmi tulajdonságok a SqlException elemnél, beleértve a Number, Class, State, LineNumber, Procedure és Server elemeket, csak az első hibát tükrözik. A következő részlet egy üzenetet nyomtat, majd ugyanabban a kötegben hibát generál. Olvas exception.Number , majd végigiterál Errors minden bejegyzés jelentéséhez:
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}");
}
}
}
A részlet így szól:
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.
A PRINT kimenet nem váltja ki a InfoMessage-et. Ha egy tétel olyan hibát ad vissza, amely miatt SqlException történik, a szolgáltató hozzáfűzi az adott tétel információs üzeneteit a(z) SqlException.Errors végéhez.
Ez a részlet csak a kontrollált hibát elfogja, hogy megmutassa a mezőit. Az alkalmazáskódban throw; segítségével dobd tovább a kivételt, hacsak a catch blokk nem végez tényleges helyreállítást. Döntéseket dokumentált hibaszámok és működési kontextus alapján hozz meg. Ne parzáld a lokalizált üzenetszöveget.
A kapcsolat állapotának ellenőrzése
Az adatbázis motor minden üzenethez egy súlyosságosztályt rendel hozzá. Az alsóbb osztályok csak tájékoztató jellegűek. A hibaként kezelt osztályok SqlException eredményeznek. A nagy súlyosságú hibák miatt a kapcsolat bezáródhat vagy megszakadhat.
Hiba után ellenőrizd State-t, mielőtt újra használod a kapcsolatot:
using var command = new SqlCommand(
"RAISERROR (N'Controlled failure.', 16, 1);",
connection);
try
{
command.ExecuteNonQuery();
}
catch (SqlException)
{
Console.WriteLine($"Connection state: {connection.State}");
}
Ne feltételezd, hogy egy kapcsolódás használható marad, mert a kivételt kezelték. Egy 16-os súlyosságú hiba általában nyitva hagyja, de a kapcsolatmegszakító hibák bezárhatják azt. A lezárt vagy megszakadt kapcsolatot el kell dobni, és egy másik kapcsolatot kell lekérni a kapcsolatkészletből. Ne alapozd az újrapróbálkozási szabályzatot pusztán a súlyosságra. A műveletnek biztonságosan megismételhetőnek kell lennie.
Hibás batch parancs azonosítása
Ha a SqlBatch hibás, SqlException.BatchCommand hivatkozhat a hozzá tartozó SqlBatchCommand. A tulajdonság null, ha a szolgáltató nem tudja a hibát egyetlen batch parancsnak tulajdonítani. A következő részlet két kötegelt parancsot tartalmaz. A második megbukik:
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}");
}
}
}
A részlet így szól:
Error 8134: Divide by zero error encountered.
Failed command: SELECT 1 / 0;
A parancsszöveg érzékeny literálisokat tartalmazhat rosszul paraméterezett alkalmazásokban. Részesítse előnyben a stabil alkalmazásművelet-neveket az éles naplókban, és alapértelmezés szerint ne naplózza a paraméterértékeket.
Felhasználói hibák irányítása az InfoMessage-re
FireInfoMessageEventOnUserErrors alapértelmezés szerint false. Amikor beállítod true, a 16-os vagy annál alacsonyabb súlyosságú hibák a InfoMessage kezelőhöz kerülnek SqlException kiváltása helyett:
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();
Nincs kivétel. A részlet így szól:
Info 50000, class 16: Routed to InfoMessage.
Ezt az opciót csak akkor használjuk, ha az alkalmazás szándékosan eseményalapú hibamodellt használ, mert a hívó kód már nem lát kivételt. A 17-es vagy annál magasabb súlyosságú hibák továbbra is riasztást váltanak ki SqlException. Egyszer állítsd be a tulajdonságot a kapcsolat konfigurációjának részeként, ahelyett, hogy az egyes parancsok előtt és után ki-be kapcsolnád.
Napló diagnosztikai kontextusa
Elég strukturált kontextust rögzíts, hogy összefüggésbe hozzuk a hibát adatok felfedése nélkül. A következő részlet naplózza az alkalmazásművelet nevét, a ClientConnectionId elemet és minden egyes SqlError mezőit.
ClientConnectionId segít összekapcsolni egy kliens hibáját a szerver- és hálózati diagnosztikával. Ez nem egy képesítés.
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}");
}
}
}
Az érték ClientConnectionId minden kapcsolatnál eltérő. A részlet hasonló kimenetet ad ki:
Operation=LoadOrders; ClientConnectionId=<connection ID>
Number=8134; Class=16; State=1; Procedure=; Line=1
Rögzítsd azt is, hogy aktív volt-e egy tranzakció, és ha BatchCommand elérhető, melyik csomagparancs hiúsult meg. Csak akkor vegyük fel a tisztított szerver- és adatbázisazonosítókat, ha a szervezeti szabályzat engedi. Ne naplózz kapcsolati karakterláncokat, hozzáférési tokeneket, jelszavakat vagy érzékeny paraméterértékeket. Használj diagnosztikai számlálókat, eseményforrás követést és a SqlClient hibakeresési útmutatót a mélyebb vizsgálathoz.
Kapcsolódó tartalom
- Térj vissza a parancsvégrehajtási munkafolyamathoz.
- Egy csoportban lévő sikertelen parancs azonosításához több parancsot hajtson végre a SqlBatch használatával.
- Korlátozott számú újrapróbálkozások hozzáadásához használj konfigurálható újrapróbálkozási logikát.
- Ha a kivételmezők nem elegendőek, használd a SqlClient hibaelhárítási útmutatóját.