Steg 4: Ansluta elastiskt till SQL med ADO.NET

Ladda ned ADO.NET

Den här artikeln ger ett C#-kodexempel som demonstrerar manuellt implementerad retry-logik. Omprovningslogiken hanterar tillfälliga fel, eller tillfälliga fel, som kan försvinna om programmet väntar och försöker operationen igen.

Viktigt!

För nya applikationer som använder Microsoft.Data.SqlClient 3.0 och senare versioner bör ni i första hand använda drivrutinens konfigurerbara försökslogik i stället för att implementera en omförsöksslinga. Börja med Konfigurera återförsökslogik i SqlClient.

Källor till tillfälliga fel är:

  • Ett kortvarigt fel i nätverket som stöder internet.
  • Ett molnsystem kan lastbalansera sina resurser när din förfrågan anländer.

ADO.NET-klasserna för anslutning till SQL Server kan också kopplas till Azure SQL Database. Ditt klientprogram kan stöta på övergående fel. Den bör återhämta sig genom att använda en begränsad återförsökspolicy som är lämplig för operationen.

Steg 1: Identifiera tillfälliga fel

Ditt program måste skilja mellan övergående fel och beständiga fel. Tillfälliga fel är felförhållanden som kan försvinna på kort tid, såsom tillfälliga nätverksproblem. Ett exempel på ett bestående fel är en felstavning av namnet på måldatabasen. I så fall kvarstår felet "No such database found" och försvinner inte snabbt.

Listan över felnummer som kategoriseras som tillfälliga fel finns tillgänglig på Felmeddelanden för SQL Database-klientapplikationer.

Steg 2: Skapa och köra exempelprogram

Det här exemplet förutsätter att .NET Framework 4.6.2 eller senare installeras. C#-kodprovet består av en fil som heter Program.cs. Koden finns i nästa avsnitt.

Steg 2.a: Samla in och kompilera kodexemplet

Du kan kompilera exemplet med följande steg:

  1. I den kostnadsfria Visual Studio Community-utgåvan skapar du ett nytt projekt från C#-konsolprogrammallen.

    • Välj Fil>Nytt>Projekt>Installerade>Mallar>Visual C#>Windows>Klassiskt skrivbord>Konsolprogram.

    • Ge projektet namnet RetryAdo2.

  2. Öppna fönstret Prieskumník riešení.

  3. Öppna Program.cs-filen.

  4. Ersätt hela innehållet i Program.cs filen med koden i följande kodblock.

  5. Välj menyn Bygg>Bygg Lösning.

Steg 2.b: Kopiera och klistra in exempelkod

Klistra in denna kod i din Program.cs fil.

Redigera sedan strängarna för serverns namn och lösenord. Du kan hitta dessa strängar i metoden som heter GetSqlConnectionString.

Anmärkning

reťazec pripojenia är inriktad på Azure SQL Database, eftersom den inkluderar det fyrteckensprefixet tcp:. Men du kan justera serversträngen för att ansluta till din SQL Server-instans.

using System;
using System.Collections.Generic;
using Microsoft.Data.SqlClient;
using System.Threading;

namespace RetryAdo2;

public class Program
{
    public static int Main(string[] args)
    {
        bool succeeded = false;
        const int totalNumberOfTimesToTry = 4;
        int retryIntervalSeconds    = 10;

        for (int tries = 1; tries <= totalNumberOfTimesToTry; tries++)
        {
            try
            {
                if (tries > 1)
                {
                    Console.WriteLine(
                        "Transient error encountered. Will begin attempt number {0} of {1} max...",
                        tries,
                        totalNumberOfTimesToTry
                    );
                    Thread.Sleep(1000 * retryIntervalSeconds);
                    retryIntervalSeconds = Convert.ToInt32(retryIntervalSeconds * 1.5);
                }
                AccessDatabase();
                succeeded = true;
                break;
            }
            catch (SqlException sqlExc) {
                if (TransientErrorNumbers.Contains(sqlExc.Number))
                {
                    Console.WriteLine("{0}: transient occurred.", sqlExc.Number);
                    continue;
                }

                Console.WriteLine(sqlExc);
                break;
            }
            catch (TestSqlException sqlExc) {
                if (TransientErrorNumbers.Contains(sqlExc.Number))
                {
                    Console.WriteLine("{0}: transient occurred. (TESTING.)", sqlExc.Number);
                    continue;
                }

                Console.WriteLine(sqlExc);
                break;
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                break;
            }
        }

        if (!succeeded) {
            Console.WriteLine("ERROR: Unable to access the database!");
            return 1;
        }

        return 0;
    }

    /// <summary>
    /// Connects to the database, reads,
    /// prints results to the console.
    /// </summary>
    static void AccessDatabase() {
        //throw new TestSqlException(4060); //(7654321);  // Uncomment for testing.

        using var sqlConnection = new SqlConnection(GetSqlConnectionString());

        using var dbCommand = sqlConnection.CreateCommand();

        dbCommand.CommandText = @"
        SELECT TOP 3
            ob.name,
            CAST(ob.object_id as NVARCHAR(32)) AS [object_id]
        FROM sys.objects AS ob
        WHERE ob.type='IT'
        ORDER BY ob.name;";
        
        sqlConnection.Open();
        var dataReader = dbCommand.ExecuteReader();

        while (dataReader.Read())
        {
            Console.WriteLine(
                "{0}\t{1}",
                dataReader.GetString(0),
                dataReader.GetString(1)
            );
        }
    }

    /// <summary>
    /// Edit the four string values in accordance with your environment.
    /// </summary>
    /// <returns>An ADO.NET connection string.</returns>
    static private string GetSqlConnectionString()
    {
        // Prepare the connection string to Azure SQL Database.
        var sqlConnectionSB = new SqlConnectionStringBuilder
        {
            // Change these values to your values.
            DataSource           = "tcp:myazuresqldbserver.database.windows.net,1433", //["Server"]
            InitialCatalog       = "MyDatabase",                                       //["Database"]
            UserID               = "MyLogin",                                          // "@yourservername"  as suffix sometimes.
            Password             = "<password>",
            // Adjust these values if you like. (ADO.NET 4.5.1 or later.)
            ConnectRetryCount    = 3,
            ConnectRetryInterval = 10, // Seconds.
            // Leave these values as they are.
            IntegratedSecurity = false,
            Encrypt            = true,
            ConnectTimeout     = 30
        };

        return sqlConnectionSB.ToString();
    }

    static List<int> TransientErrorNumbers = new()
    {
        4060, 40197, 40501, 40613, 49918, 49919, 49920, 11001
    };
}

/// <summary>
/// For testing retry logic, you can have method
/// AccessDatabase start by throwing a new
/// TestSqlException with a Number that does
/// or does not match a transient error number
/// present in TransientErrorNumbers.
/// </summary>
internal class TestSqlException : ApplicationException
{
    internal TestSqlException(int testErrorNumber)
    {
        Number = testErrorNumber;
    }

    internal int Number { get; set; }
}

Steg 2.c: Kör programmet

RetryAdo2.exe-programmet accepterar inte parametrar. För att köra .exe:

  1. Öppna ett konsolfönster i mappen där du kompilerade binärfilen RetryAdo2.exe .
  2. Kör RetryAdo2.exe utan inmatningsparametrar.
database_firewall_rules_table   245575913
filestream_tombstone_2073058421 2073058421
filetable_updates_2105058535    2105058535

Steg 3: Sätt att testa logiken för återförsök

Du kan simulera ett transient fel för att testa din retry-logik på flera sätt.

Steg 3.a: Utlöser ett testundantag

Kodexemplet innehåller:

  • En liten sekundär klass med namnet TestSqlException och en egenskap med namnet Number.
  • //throw new TestSqlException(4060); , som du kan avkommenta.

Om du avkommenterar throw-satsen och kompilerar om, kommer nästa körning av RetryAdo2.exe att ge något i stil med följande.

[C:\VS15\RetryAdo2\RetryAdo2\bin\Debug\]
>> RetryAdo2.exe
4060: transient occurred. (TESTING.)
Transient error encountered. Will begin attempt number 2 of 4 max...
4060: transient occurred. (TESTING.)
Transient error encountered. Will begin attempt number 3 of 4 max...
4060: transient occurred. (TESTING.)
Transient error encountered. Will begin attempt number 4 of 4 max...
4060: transient occurred. (TESTING.)
ERROR: Unable to access the database!

[C:\VS15\RetryAdo2\RetryAdo2\bin\Debug\]
>>

Steg 3.b: Testa igen med ett beständigt fel

För att bevisa att koden hanterar persistenta fel korrekt, kör det föregående testet igen. Istället för att använda ett verkligt övergående felnummer som 4060, använd det fiktiva numret 7654321. Programmet behandlar detta nummer som ett bestående fel och kringgår alla omförsök.

Steg 3.c: Koppla från nätverket

  1. Koppla från klientdatorn från nätverket.
    • Koppla bort nätverkskabeln från en stationär dator.
    • För en bärbar dator trycker du på funktionskombinationen av nycklar för att stänga av nätverkskortet.
  2. Starta RetryAdo2.exe, och vänta på att konsolen visar det första övergående felet, troligen 11001.
  3. Återanslut till nätverket, medan RetryAdo2.exe fortsätter att köras.
  4. Se hur konsolrapporten lyckades vid ett efterföljande återförsök.

Steg 3.d: Tillfälligt felstava servernamnet

  1. Lägg tillfälligt till 40615 som ett annat felnummer till TransientErrorNumbers, och kompilera om.
  2. Ange en brytpunkt på raden: new QC.SqlConnectionStringBuilder().
  3. Använd funktionen Redigera och fortsätt för att avsiktligt felstava servernamnet, ett par rader nedan.
    • Låt programmet köras och komma tillbaka till brytpunkten.
    • Felet 40615 inträffar.
  4. Åtgärda felstavningen.
  5. Låt programmet köras och slutföras framgångsrikt.
  6. Ta bort 40615 och kompilera om.

Nästa steg