sql >> Database >  >> RDS >> Sqlserver

Weet u wanneer u opnieuw moet proberen of mislukken bij het aanroepen van SQL Server vanuit C#?

Eén enkele SqlException (kan) verpakt meerdere SQL Server-fouten. Je kunt ze doorlopen met Errors eigendom. Elke fout is SqlError :

foreach (SqlError error in exception.Errors)

Elke SqlError heeft een Class eigenschap die u kunt gebruiken om grofweg te bepalen of u het opnieuw kunt proberen of niet (en als u het opnieuw probeert, moet u ook opnieuw verbinding maken). Van MSDN:

  • Class <10 is voor fouten in de informatie die je hebt doorgegeven en (waarschijnlijk) kun je het niet opnieuw proberen als je eerst de invoer niet corrigeert.
  • Class van 11 tot 16 zijn "gegenereerd door gebruiker", dan kunt u waarschijnlijk weer niets doen als de gebruiker eerst zijn invoer niet corrigeert. Houd er rekening mee dat klasse 16 veel tijdelijke fouten en klasse 13 is voor impasses (dankzij EvZ), dus je kunt deze klassen uitsluiten als je ze een voor een afhandelt.
  • Class van 17 tot 24 zijn generieke hardware-/softwarefouten en u kunt het opnieuw proberen. Wanneer Class 20 of hoger is, moet u de verbinding opnieuw tot stand brengen te. 22 en 23 kunnen ernstige hardware-/softwarefouten zijn, 24 geeft een mediafout aan (iets waarvan de gebruiker moet worden gewaarschuwd, maar u kunt het opnieuw proberen als het slechts een "tijdelijke" fout was).

Een meer gedetailleerde beschrijving van elke klas vind je hier.

Als je fouten met hun klasse afhandelt, hoef je in het algemeen niet elke fout precies te weten (met behulp van error.Number eigenschap of exception.Number wat slechts een snelkoppeling is voor de eerste SqlError in die lijst). Dit heeft het nadeel dat u het opnieuw kunt proberen als het niet nuttig is (of als de fout niet kan worden hersteld). Ik stel voor een aanpak in twee stappen :

  • Controleer op bekende foutcodes (lijst foutcodes met SELECT * FROM master.sys.messages ) om te zien wat u wilt aanpakken (weten hoe). Die weergave bevat berichten in alle ondersteunde talen, dus u moet ze mogelijk filteren op msglangid kolom (bijvoorbeeld 1033 voor Engels).
  • Voor al het andere vertrouwt u op de foutklasse, opnieuw proberen wanneer Class is 13 of hoger dan 16 (en opnieuw verbinden indien 20 of hoger).
  • Fouten met een ernst hoger dan 21 (22, 23 en 24) zijn ernstige fouten en een beetje wachten zal die problemen niet oplossen (database zelf kan ook beschadigd zijn).

Een woord over hogere klassen. Het is niet eenvoudig om met deze fouten om te gaan en hangt van veel factoren af ​​(waaronder risicobeheer .). voor uw aanvraag). Als een eenvoudige eerste stap zou ik 22, 23 en 24 niet opnieuw proberen wanneer ik een schrijfbewerking probeer:als database, bestandssysteem of media ernstig beschadigd zijn, kan het schrijven van nieuwe gegevens de gegevensintegriteit nog meer verslechteren (SQL Server is uiterst voorzichtig om breng de database niet in gevaar voor een query, zelfs niet in kritieke omstandigheden). Een beschadigde server, het hangt af van uw DB-netwerkarchitectuur, kan zelfs hot-swapped worden (automatisch, na een bepaalde tijd, of wanneer een bepaalde trigger wordt geactiveerd). Raadpleeg en werk altijd dicht bij uw DBA.

De strategie voor het opnieuw proberen hangt af van de fout die u oplost:middelen vrijmaken, wachten tot een in behandeling zijnde bewerking is voltooid, een alternatieve actie ondernemen, enz. Over het algemeen moet u het alleen opnieuw proberen als alle fouten zijn "retry-able":

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;
    }
}

Wikkel alles in try /finally aansluiting op de juiste manier af te voeren. Met deze simpele, nep-naïeve CanRetry() functie:

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...
}

Enkele behoorlijk lastige manieren om hier een lijst met niet-kritieke fouten te vinden.

Meestal sluit ik al deze (boilerplate) code in één methode in (waar ik kan verbergen alle vuile dingen gedaan om verbinding te maken/verwijderen/opnieuw te maken) met deze handtekening:

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

Zo te gebruiken:

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

Houd er rekening mee dat skeleton (opnieuw proberen bij fout) ook kan worden gebruikt als u niet met SQL Server werkt (eigenlijk kan het worden gebruikt voor veel andere bewerkingen zoals I/O en netwerkgerelateerde dingen, dus ik zou willen voorstellen om een ​​algemene functie te schrijven en om het uitgebreid te hergebruiken).



  1. Zijn een CASE-statement en een DECODE equivalent?

  2. SQL-query om het N-de hoogste salaris uit een salaristabel te vinden

  3. Hoe SQLite Nullif() werkt

  4. Installeer Mtop (MySQL Database Server Monitoring) in RHEL/CentOS 6/5/4, Fedora 17-12