sql >> Database teknologi >  >> RDS >> Sqlserver

Ved du, hvornår du skal prøve igen eller fejle, når du kalder SQL Server fra C#?

Én enkelt SqlException (kan) ombryder flere SQL Server-fejl. Du kan gentage dem med Errors ejendom. Hver fejl er SqlError :

foreach (SqlError error in exception.Errors)

Hver SqlError har en Class egenskab, du kan bruge til groft at bestemme, om du kan prøve igen eller ej (og hvis du prøver igen, hvis du også skal genskabe forbindelsen). Fra MSDN:

  • Class <10 er for fejl i oplysninger, du har givet, så (sandsynligvis) kan du ikke prøve igen, hvis du først ikke retter indtastninger.
  • Class fra 11 til 16 er "genereret af bruger", så kan du sandsynligvis igen ikke gøre noget, hvis brugeren først ikke retter sine input. Bemærk venligst, at klasse 16 indeholder mange midlertidige fejl og klasse 13 er til dødvande (takket være EvZ), så du kan udelukke disse klasser, hvis du håndterer dem én efter én.
  • Class fra 17 til 24 er generiske hardware/software fejl, og du kan prøve igen. Når Class er 20 eller højere, skal du genoprette forbindelsen også. 22 og 23 kan være alvorlige hardware/software fejl, 24 angiver en mediefejl (noget brugeren skal advares, men du kan prøve igen, hvis det blot var en "midlertidig" fejl).

Du kan finde en mere detaljeret beskrivelse af hver klasse her.

Generelt, hvis du håndterer fejl med deres klasse, behøver du ikke at kende præcis hver fejl (ved hjælp af error.Number ejendom eller exception.Number som blot er en genvej til den første SqlError på den liste). Dette har den ulempe, at du kan prøve igen, når det ikke er nyttigt (eller fejlen ikke kan gendannes). Jeg vil foreslå en to-trins tilgang :

  • Se efter kendte fejlkoder (liste fejlkoder med SELECT * FROM master.sys.messages ) for at se, hvad du vil håndtere (at vide hvordan). Denne visning indeholder meddelelser på alle understøttede sprog, så du skal muligvis filtrere dem efter msglangid kolonne (f.eks. 1033 for engelsk).
  • For alt andet stole på fejlklasse, prøv igen når Class er 13 eller højere end 16 (og genopretter forbindelse, hvis 20 eller højere).
  • Fejl med sværhedsgrad højere end 21 (22, 23 og 24) er alvorlige fejl, og lidt ventetid løser ikke disse problemer (databasen kan også være beskadiget).

Et ord om højere klasser. Hvordan man håndterer disse fejl er ikke let, og det afhænger af mange faktorer (herunder risikostyring til din ansøgning). Som et simpelt første skridt ville jeg ikke prøve igen for 22, 23 og 24, når jeg forsøger en skriveoperation:hvis database, filsystem eller medier er alvorligt beskadiget, kan skrivning af nye data forringe dataintegriteten endnu mere (SQL Server er ekstremt forsigtig med at kompromitter ikke DB for en forespørgsel, selv under kritiske omstændigheder). En beskadiget server, det afhænger af din DB-netværksarkitektur, kan endda blive hot-swapped (automatisk, efter et bestemt tidsrum, eller når en specificeret trigger udløses). Rådfør dig og arbejd altid tæt på din DBA.

Strategien for at prøve igen afhænger af den fejl, du håndterer:frigør ressourcer, vent på, at en afventende handling er fuldført, foretag en alternativ handling osv. Generelt bør du kun prøve igen, hvis alle fejl kan "gentages":

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

Pak alt ind i try /finally at bortskaffe forbindelsen korrekt. Med denne simple-falske-naive CanRetry() funktion:

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

Nogle ret vanskelige måder at finde en liste over ikke-kritiske fejl her.

Normalt indlejrer jeg al denne (boilerplate) kode i én metode (hvor jeg kan skjule alle de snavsede ting gjort for at skabe/disponere/genskabe forbindelse) med denne signatur:

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

Skal bruges sådan her:

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

Bemærk venligst, at skelet (forsøg igen ved fejl) også kan bruges, når du ikke arbejder med SQL Server (faktisk kan det bruges til mange andre operationer som I/O og netværksrelaterede ting, så jeg vil foreslå at skrive en generel funktion og genbruge det i vid udstrækning).



  1. TIME() Eksempler – MySQL

  2. Oracle Rows to Column Transformation

  3. Forespørgsel ekstremt langsom i kode, men hurtig i SSMS

  4. Konverter 'smalldatetime' til 'datetime' i SQL Server (T-SQL-eksempler)