sql >> Databáze >  >> RDS >> Sqlserver

Víte, kdy opakovat nebo selhat při volání SQL Server z C#?

Jedna jediná SqlException (může) zabalí více chyb SQL Server. Můžete je iterovat pomocí Errors vlastnictví. Každá chyba je SqlError :

foreach (SqlError error in exception.Errors)

Každá SqlErrorClass vlastnost, kterou můžete použít k přibližnému určení, zda to můžete zkusit znovu nebo ne (a v případě, že to zkusíte znovu, pokud budete muset také znovu vytvořit připojení). Z MSDN:

  • Class <10 je pro chyby v informacích, které jste předali, pak (pravděpodobně) nemůžete opakovat, pokud nejprve neopravíte vstupy.
  • Class od 11 do 16 jsou "generovány uživatelem", pak pravděpodobně opět nemůžete nic dělat, pokud uživatel nejprve neopraví své vstupy. Upozorňujeme, že třída 16 obsahuje mnoho dočasných chyby a třída 13 je pro uváznutí (díky EvZ), takže můžete tyto třídy vyloučit, pokud je budete zpracovávat jednu po druhé.
  • Class od 17 do 24 jsou obecné hardwarové/softwarové chyby a můžete to zkusit znovu. Když Class je 20 nebo vyšší, musíte obnovit připojení také. 22 a 23 mohou být závažné chyby hardwaru/softwaru, 24 označuje chybu média (uživatel by měl být na něco upozorněn, ale můžete to zkusit znovu, pokud se jednalo pouze o „dočasnou“ chybu).

Podrobnější popis každé třídy naleznete zde.

Obecně platí, že pokud řešíte chyby s jejich třídou, nebudete potřebovat znát přesně každou chybu (pomocí error.Number vlastnost nebo exception.Number což je jen zkratka pro první SqlError v tom seznamu). To má tu nevýhodu, že to můžete zkusit znovu, když to není užitečné (nebo chybu nelze obnovit). Navrhoval bych dvoukrokový přístup :

  • Vyhledejte známé chybové kódy (vypište chybové kódy pomocí SELECT * FROM master.sys.messages ), abyste viděli, co chcete zvládnout (a vědět jak). Toto zobrazení obsahuje zprávy ve všech podporovaných jazycích, takže je možná budete muset filtrovat podle msglangid sloupec (například 1033 pro angličtinu).
  • Ve všem ostatním se spolehněte na třídu chyb, opakujte pokus při Class je 13 nebo vyšší než 16 (a opětovné připojení, pokud je 20 nebo vyšší).
  • Chyby se závažností vyšší než 21 (22, 23 a 24) jsou závažné chyby a krátké čekání tyto problémy nevyřeší (může být poškozena i samotná databáze).

Jedno slovo o vyšších třídách. Řešení těchto chyb není jednoduché a závisí na mnoha faktorech (včetně řízení rizik pro vaši aplikaci). Jako jednoduchý první krok bych při pokusu o operaci zápisu nezkoušel opakovat 22, 23 a 24:pokud jsou databáze, souborový systém nebo médium vážně poškozeny, zápis nových dat může ještě více zhoršit integritu dat (SQL Server je extrémně opatrný, aby neohrožujte DB pro dotaz ani za kritických okolností). Poškozený server, v závislosti na vaší síťové architektuře DB, může být dokonce vyměněn za chodu (automaticky, po určité době nebo při spuštění zadaného spouštěče). Vždy konzultujte a pracujte v blízkosti svého DBA.

Strategie opakování závisí na chybě, kterou řešíte:uvolněte zdroje, počkejte na dokončení čekající operace, proveďte alternativní akci atd. Obecně byste to měli opakovat, pouze pokud všechny chyby lze opakovat:

bool rebuildConnection = true; // First try connection must be open

for (int i=0; i < MaximumNumberOfRetries; ++i) {
    try {
        // (Re)Create connection to SQL Server
        if (rebuildConnection) {
            if (connection != null)
                connection.Dispose();

            // Create connection and open it...
        }

        // Perform your task

        // No exceptions, task has been completed
        break;
    }
    catch (SqlException e) {
        if (e.Errors.Cast<SqlError>().All(x => CanRetry(x))) {
            // What to do? Handle that here, also checking Number property.
            // For Class < 20 you may simply Thread.Sleep(DelayOnError);

            rebuildConnection = e.Errors
                .Cast<SqlError>()
                .Any(x => x.Class >= 20);

            continue; 
        }

        throw;
    }
}

Vše zabalte do try /finally správně zlikvidovat připojení. S tímto jednoduchým, falešným a naivním CanRetry() funkce:

private static readonly int[] RetriableClasses = { 13, 16, 17, 18, 19, 20, 21, 22, 24 };

private static bool CanRetry(SqlError error) {
    // Use this switch if you want to handle only well-known errors,
    // remove it if you want to always retry. A "blacklist" approach may
    // also work: return false when you're sure you can't recover from one
    // error and rely on Class for anything else.
    switch (error.Number) {
        // Handle well-known error codes, 
    }

    // Handle unknown errors with severity 21 or less. 22 or more
    // indicates a serious error that need to be manually fixed.
    // 24 indicates media errors. They're serious errors (that should
    // be also notified) but we may retry...
    return RetriableClasses.Contains(error.Class); // LINQ...
}

Zde je několik docela složitých způsobů, jak najít seznam nekritických chyb.

Obvykle vložím celý tento (boilerplate) kód jednou metodou (kde mohu skrýt všechny špinavé věci hotovo vytvořit/zrušit/obnovit připojení) s tímto podpisem:

public static void Try(
    Func<SqlConnection> connectionFactory,
    Action<SqlCommand> performer);

K použití takto:

Try(
    () => new SqlConnection(connectionString),
    cmd => {
             cmd.CommandText = "SELECT * FROM master.sys.messages";
             using (var reader = cmd.ExecuteReader()) {
                 // Do stuff
         }
    });

Vezměte prosím na vědomí, že kostru (opakování při chybě) lze použít i tehdy, když nepracujete se serverem SQL (ve skutečnosti ji lze použít pro mnoho dalších operací, jako jsou I/O a věci související se sítí, takže bych navrhoval napsat obecnou funkci a rozsáhle jej znovu používat).



  1. Parametrizované dotazy pomocí psycopg2 / Python DB-API a PostgreSQL

  2. Webinář:Bankovnictví na Postgres – úvahy o finanční aplikaci [Následovat]

  3. Naformátujte číslo jako procento v Oracle

  4. Soubory DSN a software IRI