Schritt 4: Herstellen stabiler SQL-Verbindungen mit ADO.NET

Herunterladen von ADO.NET

Dieser Artikel bietet ein Beispiel für einen C#-Code, das manuell implementierte Wiederholungslogik demonstriert. Die Retry-Logik behandelt temporäre Fehler oder vorübergehende Fehler, die sich beheben können, wenn das Programm wartet und die Operation erneut versucht.

Von Bedeutung

Für neue Anwendungen, die Microsoft.Data.SqlClient 3.0 oder höher verwenden, sollte die konfigurierbare Wiederholungslogik des Treibers bevorzugt werden, anstatt selbst eine Wiederholungsschleife zu implementieren. Beginnen Sie mit Configure Retry Logic in SqlClient.

Quellen vorübergehender Fehler sind u. a.:

  • Ein kurzer Ausfall des Netzwerks, das das Internet unterstützt.
  • Ein Cloud-System könnte seine Ressourcen ausbalancieren, wenn Ihre Anfrage eintrifft.

Die ADONET-Klassen zur Verbindung mit SQL Server können sich ebenfalls mit der Azure SQL-Datenbank verbinden. Dein Client-Programm kann vorübergehende Fehler haben. Es sollte sich mithilfe einer begrenzten Wiederholungsstrategie wiederherstellen lassen, die für den Vorgang geeignet ist.

Schritt 1: Ermitteln vorübergehender Fehler

Ihr Programm muss zwischen vorübergehenden und persistenten Fehlern unterscheiden. Vorübergehende Fehler sind Fehlerzustände, die sich in kurzer Zeit beheben können, wie zum Beispiel vorübergehende Netzwerkprobleme. Ein Beispiel für einen persistenten Fehler ist eine falsche Rechtschreibung des Zieldatenbank-Namens. In diesem Fall bleibt der Fehler "Keine solche Datenbank gefunden" bestehen und verschwindet nicht schnell.

Die Liste der Fehlernummern, die als vorübergehende Fehler kategorisiert sind, ist unter Fehlermeldungen für SQL-Datenbank-Clientanwendungen verfügbar.

Schritt 2: Erstellen und Ausführen einer Beispielanwendung

Bei dem Beispiel wird davon ausgegangen, dass .NET Framework 4.6.2 oder höher installiert ist. Das C#-Codebeispiel besteht aus einer Datei namens Program.cs. Den Code dazu finden Sie im nächsten Abschnitt.

Schritt 2.a: Erfassen und Kompilieren des Codebeispiels

Sie können das Beispiel mit den folgenden Schritten kompilieren:

  1. Erstellen Sie in der kostenlosen Visual Studio Community Editionein neues Projekt mit der Vorlage für die C#-Konsolenanwendung.

    • Wählen Sie Datei>Neu>Projekt>Installiert>Vorlagen>Visual C#>Windows>Klassischer Desktop>Konsolenanwendung aus.

    • Benennen Sie das Projekt RetryAdo2.

  2. Öffnen Sie den Bereich „Projektmappen-Explorer“.

    • Beachten Sie den Namen des Projekts.
    • Fügen Sie eine NuGet Abhängigkeit vom Microsoft.Data.SqlClient-Paket in Ihrem Projekt hinzu.
    • Sehen Sie sich den Namen der Program.cs Datei an.
  3. Öffnen Sie die Datei Program.cs .

  4. Ersetzen Sie den gesamten Inhalt der Program.cs Datei durch den Code im folgenden Codeblock.

  5. Wählen Sie das Menü Build>Build Solution aus.

Schritt 2.b: Kopieren und Einfügen des Beispielcodes

Fügen Sie diesen Code in Ihre Program.cs Datei ein.

Dann bearbeite ich die Zeichenketten für Servernamen und Passwort. Diese Strings findest du in der Methode namens GetSqlConnectionString.

Anmerkung

Die Verbindungszeichenfolge ist auf Azure SQL-Datenbank ausgerichtet, da sie das vierstellige Präfix tcp:enthält. Aber du kannst den Serverstring so anpassen, dass er sich mit deiner SQL Server-Instanz verbindet.

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

Schritt 2.c: Ausführen des Programms

Die RetryAdo2.exe ausführbare Datei akzeptiert keine Parameter. Um .exe auszuführen:

  1. Öffne ein Konsolenfenster in dem Ordner, in dem du die RetryAdo2.exe Binärdatei kompiliert hast.
  2. Führe RetryAdo2.exe ohne Eingabeparameter aus.
database_firewall_rules_table   245575913
filestream_tombstone_2073058421 2073058421
filetable_updates_2105058535    2105058535

Schritt 3: Möglichkeiten um Ihre Wiederholungslogik zu testen

Man kann einen vorübergehenden Fehler simulieren, um die Logik des Wiederholens auf verschiedene Arten zu testen.

Schritt 3.a: Auslösen einer Testausnahme

Das Codebeispiel beinhaltet:

  • Eine kleine zweite Klasse namens TestSqlException mit einer Eigenschaft namens Number.
  • //throw new TestSqlException(4060); , wofür Sie die Auskommentierung aufheben können.

Wenn du die Throw-Anweisung entkommentierst und neu kompilierst, liefert der nächste Durchlauf RetryAdo2.exe etwas Ähnliches wie das folgende aus.

[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\]
>>

Schritt 3.b: Erneutes Testen mit einem beständigen Fehler

Um zu beweisen, dass der Code persistente Fehler korrekt handhabt, führen Sie den vorherigen Test erneut durch. Anstatt eine reale transientistische Fehlerzahl wie 4060 zu verwenden, verwenden Sie die fiktive Zahl 7654321. Das Programm behandelt diese Zahl als persistierenden Fehler und umgeht jeden erneuten Versuch.

Schritt 3.c: Trennen vom Netzwerk

  1. Trennen Sie den Clientcomputer vom Netzwerk.
    • Bei einem Desktop-Computer stecken Sie das Netzwerkkabel aus.
    • Bei einem Laptop drücken Sie die Tastenkombination um den Netzwerkadapter auszuschalten.
  2. Starte RetryAdo2.exe, und warte, bis die Konsole den ersten transienten Fehler anzeigt, wahrscheinlich 11001.
  3. Stellen Sie die Verbindung mit dem Netzwerk wieder her, während RetryAdo2.exe weiter ausgeführt wird.
  4. Beobachten Sie bei einem späteren erneuten Versuch, wie die Konsole den Erfolg meldet.

Schritt 3.d: Temporäres Falschschreiben des Servernamens

  1. Füge vorübergehend 40615 als weitere Fehlernummer hinzu TransientErrorNumbersund kompiliere erneut.
  2. Setzen Sie einen Haltepunkt in der Zeile: new QC.SqlConnectionStringBuilder().
  3. Verwenden Sie die Funktion Bearbeiten und fortfahren, um einige Zeilen tiefer den Servernamen absichtlich falsch zu schreiben.
    • Lassen Sie das Programm laufen und kehren Sie zu Ihrem Haltepunkt zurück.
    • Der Fehler „40615“ tritt auf.
  4. Beheben Sie den Rechtschreibfehler.
  5. Lassen Sie das Programm laufen und erfolgreich abschließen.
  6. Entfernen Sie „40615“ und kompilieren Sie erneut.

Nächster Schritt