Verbinden Sie sich mit einer Datenquelle mit Microsoft. Data.SqlClient

SqlConnectionstellt eine logische Verbindung zu SQL Server, Azure SQL-Datenbank, Azure SQL Managed Instance oder SQL-Datenbank in Microsoft Fabric dar. Das Öffnen des Objekts erhält eine physische Verbindung aus dem Verbindungspool, wenn eine verfügbar ist. Das Schließen oder Entsorgen stellt diese physische Verbindung zum Pool wieder her.

Nutze kurzlebige SqlConnection Objekte für Arbeitseinheiten. Lass keine globale Verbindung für die Anwendung offen.

Prerequisites

  • Das .NET 10 SDK oder ein späteres unterstütztes .NET SDK. Das SDK läuft unter Windows, Linux und macOS. Es erstellt außerdem die Buildversion jedes Samples für .NET Framework, die nur unter Windows läuft und .NET Framework 4.8.1 benötigt.
  • Eine Datenbank, mit der Sie sich in SQL Server, Azure SQL-Datenbank, Azure SQL Managed Instance oder SQL-Datenbank in Microsoft Fabric verbinden können.

Erstelle das Projekt

Microsoft. Data.SqlClient 7.0 und spätere Versionen benötigen das versionsangepasste Microsoft.Data.SqlClient.Extensions.Azure Paket für vom Treiber bereitgestellte Microsoft Entra ID-Authentifizierungsmodi, wie zum Beispiel Active Directory Default. Wenn deine App nur SQL-Authentifizierung oder integrierte Windows-Authentifizierung verwendet, kannst du das weglassen.

Führe in einem leeren Ordner folgende Befehle aus:

dotnet new console --framework net10.0 --name SqlClientConnect
cd SqlClientConnect
dotnet add package Microsoft.Data.SqlClient --version 7.1.1
dotnet add package Microsoft.Data.SqlClient.Extensions.Azure --version 7.1.1

Die Beispiele in diesem Artikel lesen den Verbindungszeichenfolge aus der SQL_CONNECTION_STRING Umgebungsvariable. Stell es ein, bevor du ein Beispiel ausführst. Für Verbindungszeichenfolge-Formate siehe Connection strings.

Jede Probe außer der Transaktionsprobe ist ein vollständiges Programm. Um eins auszuführen, ersetzen Sie den Inhalt von Program.cs dadurch und führen Sie dann dotnet run aus. Das Transaktionsbeispiel ist ein Fragment für Ihre eigene Implementierung. Der .NET-Tab verwendet Anweisungen der obersten Ebene, implizite using-Direktiven und await using. Der .NET Framework-Tab verwendet C# 7.3, da await using nicht vorhanden ist, verwendet er stattdessen using-Anweisungen.

Eröffne eine Verbindung

Rufen Sie OpenAsync in asynchronem Code oder Open in synchronem Code auf. Deklariere die Verbindung mit await using oder using so, dass sie entsorgt wird, wenn der Code den Scope verlässt, selbst wenn eine Ausnahme ausgelöst wird.

Mit integrierter Windows-Authentifizierung nutzt die Verbindung die Identität des Prozesses. Bei einer Webanwendung oder einem Windows-Dienst ist das der Anwendungspool oder das Servicekonto, nicht der angemeldete Benutzer. Weitere Informationen finden Sie unter Wählen Sie ein Authentifizierungsmuster aus.

Dieses vollständige Programm öffnet eine Verbindung, meldet jede Zustandsänderung und liest zwei Servereigenschaften:

using Microsoft.Data.SqlClient;

string connectionString =
    Environment.GetEnvironmentVariable("SQL_CONNECTION_STRING")
    ?? throw new InvalidOperationException(
        "Set the SQL_CONNECTION_STRING environment variable.");

await using var connection = new SqlConnection(connectionString);
connection.StateChange += (_, e) =>
    Console.WriteLine($"State: {e.OriginalState} to {e.CurrentState}");

await connection.OpenAsync();

await using var command = new SqlCommand(
    """
    SELECT CAST(SERVERPROPERTY('Edition') AS nvarchar(128)),
           CAST(SERVERPROPERTY('EngineEdition') AS int);
    """,
    connection);

await using (SqlDataReader reader = await command.ExecuteReaderAsync())
{
    await reader.ReadAsync();
    Console.WriteLine($"Edition: {reader.GetString(0)}");
    Console.WriteLine($"EngineEdition: {reader.GetInt32(1)}");
}

Im Vergleich zur SQL Server 2025 Enterprise Edition lautet das Ergebnis:

State: Closed to Open
Edition: Enterprise Edition: Core-based Licensing (64-bit)
EngineEdition: 3
State: Open to Closed

Die letzte Zeile erscheint, wenn die Verbindung am Ende des Programms freigegeben wird.

Verbindungen schließen und freigeben

Schließe immer eine Verbindung, wenn du fertig bist, damit die physische Verbindung in den Pool zurückgegeben wird. Die using- und await using-Anweisungen entsorgen die Verbindung, wenn der Code den Block verlässt, einschließlich wenn eine unbehandelte Ausnahme geworfen wird. Sie können auch Dispose oder Close direkt anrufen. Close und Dispose haben denselben Effekt auf die physische Verbindung.

Folgen Sie diesen Regeln:

  • Entsorgen Sie Reader und Befehlsobjekte vor der zugehörigen Verbindung.
  • Verlassen Sie sich nicht darauf, dass die Garbage Collection eine Verbindung an den Pool zurückgibt. Eine Verbindung, die außer Gültigkeitsbereich gerät, ohne geschlossen zu werden, wird nicht umgehend zurückgegeben, und dem Pool können die Verbindungen ausgehen.
  • Rufen Sie Close oder Dispose nicht für eine Verbindung, einen Reader oder ein anderes verwaltetes Objekt in einem Klassenfinalizer auf. Ein Finalizer sollte nur unverwaltete Ressourcen freigeben, die seine Klasse direkt besitzt. Weitere Informationen finden Sie unter Garbage Collection.
  • Danach können Sie Open für dasselbe Objekt erneut aufrufen. Danach Dispose erstellen Sie ein neues Objekt.

Mit aktiviertem Pooling gibt das Schließen einer Verbindung diese zurück in den Pool, ohne die Netzwerkverbindung zu schließen. Der Pool setzt den Sitzungszustand zurück, wenn die Verbindung wiederverwendet wird. Weitere Informationen finden Sie unter SQL Server Connection Pooling.

Verwenden von asynchronen APIs

Verwenden Sie asynchrone Aufrufe für netzwerkgebundene Datenbankarbeiten in Webservern, Diensten, Benutzeroberflächen und Arbeitern:

  • OpenAsync(cancellationToken)
  • ExecuteNonQueryAsync(cancellationToken)
  • ExecuteReaderAsync(cancellationToken)
  • ExecuteScalarAsync(cancellationToken)
  • ReadAsync(cancellationToken)

Starten Sie keine weitere Operation an einer Verbindung, einem Befehl oder einem Leser, bevor die aktuelle asynchrone Operation abgeschlossen ist.

Stornierung und Timeouts anwenden

Gib das CancellationToken des Aufrufers bei jedem asynchronen Datenbankaufruf weiter. Eine Stornierung fordert den Fahrer auf, die ausstehenden Arbeiten einzustellen, aber der Abschluss der Stornierung ist nicht unbedingt sofort. Verwende weiterhin begrenzte Zeitlimits für Verbindungen und Befehle.

Diese Steuerungen haben getrennte Umfangsbereiche:

Steuerung Umfang
Connect Timeout Verbindungsaufbau oder das Warten auf eine gepoolte Verbindung
SqlCommand.CommandTimeout Eine Befehlsausführung
CancellationToken Vom Anrufer angeforderte Abbruch einer asynchronen Operation

Ein Timeout oder eine Stornierung beweist nicht, dass der Server eine Operation zurückgesetzt hat. Verwenden Sie eine Transaktion, wenn mehrere Änderungen als eine Einheit festgeschrieben oder zurückgesetzt werden müssen, und treffen Sie Wiederholungsentscheidungen auf Grundlage der Idempotenz des Vorgangs und des Transaktionsergebnisses.

Verbindungsstatus verfolgen

Die Eigenschaft State liefert einen Schnappschuss aus der ConnectionState Aufzählung.

State Meaning
Closed Die logische Verbindung ist nicht offen.
Connecting Eine offene Operation ist im Gange.
Open Die logische Verbindung ist offen.

Das StateChange Ereignis wird ausgelöst, wenn sich der Zustand ändert. Sein StateChangeEventArgsArgument besitzt zwei Eigenschaften:

  • OriginalState ist der Zustand vor der Änderung.
  • CurrentState ist der Zustand nach der Änderung.

Der Treiber meldet die Übergänge von Open zu Open und von Closed zu Closed, wie das Beispiel in Open a connection zeigt. Betrachten Sie Executing, Fetching oder Broken nicht als Phasen des Anwendungslebenszyklus.

Verwenden Sie State nicht vor jedem Befehl als Integritätsprüfung. Das Netzwerk kann nach jeder Überprüfung ausfallen. Führen Sie die Operation aus und behandeln Sie die resultierende Ausnahme. Nutze StateChange für Diagnosen, nicht zur Koordination der gleichzeitigen Arbeit.

Informationsnachrichten empfangen

Das InfoMessage Event liefert Nachrichten, die der Server sendet, ohne einen Fehler auszulösen. Diese Nachrichten enthalten PRINT Ausgaben, RAISERROR Nachrichten mit einer Schwerestufe von 10 oder weniger sowie Serverwarnungen. Fehler mit einer Schwere von 11 oder höher lösen stattdessen SqlException aus. Für Schweregrade siehe Fehlerschweregrade der Datenbank-Engine.

Der Ereignishandler erhält ein SqlInfoMessageEventArgs Objekt. Seine Errors-Eigenschaft enthält eine Sammlung von SqlError-Objekten. Jede enthält die Nachrichtennummer, den Text, die Schweregradsklasse, das Verfahren und die Zeilennummer.

using Microsoft.Data.SqlClient;

string connectionString =
    Environment.GetEnvironmentVariable("SQL_CONNECTION_STRING")
    ?? throw new InvalidOperationException(
        "Set the SQL_CONNECTION_STRING environment variable.");

await using var connection = new SqlConnection(connectionString);
connection.InfoMessage += (_, e) =>
{
    foreach (SqlError error in e.Errors)
    {
        Console.WriteLine(
            $"Message {error.Number}, severity {error.Class}: {error.Message}");
    }
};

await connection.OpenAsync();

await using var command = new SqlCommand(
    """
    PRINT 'Starting the batch.';
    RAISERROR('Processed %d rows.', 10, 1, 42);
    """,
    connection);

await command.ExecuteNonQueryAsync();

Die Ausgabe lautet:

Message 0, severity 0: Starting the batch.
Message 50000, severity 0: Processed 42 rows.

Der Server meldet eine RAISERROR Schwerestufe von 10 als Klasse 0, also zeigt SqlError.Class0 für beide Nachrichten an.

Nutzerfehler als Informationsnachrichten behandeln

Standardmäßig meldet der Treiber einen Fehler mit einem Schweregrad von 11 oder höher, indem er eine SqlException auslöst. Diese Ausnahme bedeutet nicht, dass der Server den Batch gestoppt hat. Viele Fehler, darunter RAISERROR mit einem Schweregrad von 16, ermöglichen es dem Server, die verbleibenden Anweisungen auszuführen, sodass spätere Anweisungen Daten ändern können, selbst wenn Ihr Code die Ausnahme abfängt. Um diese Fehler als Nachrichten statt Ausnahmen zu empfangen, setzen Sie FireInfoMessageEventOnUserErrors auf true. Der Treiber sendet dann Fehler mit einem Schweregrad von 16 oder weniger an das InfoMessage Event, anstatt eine Ausnahme auszulösen. Diese Eigenschaft verändert, wie der Treiber Fehler meldet. Es ändert nicht, welche Anweisungen der Server ausführt.

using Microsoft.Data.SqlClient;

string connectionString =
    Environment.GetEnvironmentVariable("SQL_CONNECTION_STRING")
    ?? throw new InvalidOperationException(
        "Set the SQL_CONNECTION_STRING environment variable.");

await using var connection = new SqlConnection(connectionString)
{
    FireInfoMessageEventOnUserErrors = true,
};
connection.InfoMessage += (_, e) =>
{
    foreach (SqlError error in e.Errors)
    {
        Console.WriteLine(
            $"Message {error.Number}, severity {error.Class}: {error.Message}");
    }
};

await connection.OpenAsync();

await using var command = new SqlCommand(
    """
    RAISERROR('Row 7 failed validation.', 16, 1);
    SELECT 'The batch continued.';
    """,
    connection);

await using (SqlDataReader reader = await command.ExecuteReaderAsync())
{
    do
    {
        while (await reader.ReadAsync())
        {
            Console.WriteLine(reader.GetString(0));
        }
    }
    while (await reader.NextResultAsync());
}

Die Ausgabe lautet:

Message 50000, severity 16: Row 7 failed validation.
The batch continued.

Fehler mit einer Schweregrad von 17 oder höher führen unabhängig von dieser Einstellung immer noch zu einem SqlException. Wenn Sie diese Eigenschaft aktivieren, ist Ihr Handler dafür verantwortlich, Fehler zu erkennen. Code, der für jeden Fehler eine Ausnahme erwartet, sieht keine Ausnahme.

Melde die Anwendungsidentität

Ab Microsoft.Data.SqlClient 7.1.0 kann ein Framework, Tool oder andere Middleware melden, welche Anwendung eine Verbindung eröffnet hat. Setze RegisteredApplication auf einen RegisteredApplication Wert, bevor du die Verbindung öffnest. Der Treiber sendet den Wert einmal an den Server, also beim Login.

Folgen Sie diesen Regeln:

  • Stellen Sie den Wert über die Eigenschaft fest. Dafür existiert kein Schlüsselwort für die Verbindungszeichenfolge.
  • Setze den Wert, bevor du die Verbindung öffnest. Wenn man sie einstellt, während die Verbindung geöffnet wird oder geöffnet ist, wird ein InvalidOperationException ausgelöst.
  • Behandle den Wert als Telemetrie. Der Client liefert den Wert und kann jede Kennung angeben, und der Server authentifiziert sie nicht.
  • Erwarten Sie, dass gepoolte Verbindungen die ursprüngliche Identität melden. Der Wert ist nicht Teil des Pool-Schlüssels. Eine vom Pool bediente Verbindung meldet die Identität der logischen Verbindung, die zur Erstellung der physischen Verbindung geführt hat.

Caution

Verwenden Sie RegisteredApplication niemals für Autorisierung oder andere Sicherheitsentscheidungen. Jeder Client kann jede Kennung melden.

Wählen Sie einen Bezeichner

Der Standardwert ist Unknown, also 0. Die Aufzählung hat einen zugrunde liegenden Typ von ushort, sodass die Identifikatoren von 0 bis 0xFFFFreichen.

Bereich Verwenden
0x0001 bis 0x7FFF Microsoft-definierte Anwendungen. Benannte Enumerationsmitglieder verwenden diesen Bereich.
0x8000 bis 0xBFFF Für den Einsatz in kleinem Maßstab vorgesehen.
0xC000 bis 0xFFFF Öffentliche Nutzung und Nutzung durch Entwickler.

Die benannten Mitglieder sind EntityFrameworkCore, SemanticKernel, ManagementStudio, SqlManagementObjects, DataTierApplicationFramework, SqlToolsService, AspNetCoreDistributedSqlServerCache, EntityFramework, AzureFunctionsSqlExtension, SqlPackage, OrleansAdoNet, DurableTaskSqlServer und DataApiBuilder. Um eine Kennung ohne benanntes Mitglied zu melden, wandeln Sie die Zahl in RegisteredApplication um.

Der RegisteredApplication Typ und die Eigenschaft sind nicht CLS-konform, da die Aufzählung ushort verwendet. C#-Code kann sie direkt verwenden.

Setzen Sie die Kennung

Dieses Programm meldet eine Kennung aus dem Entwicklerbereich und zeigt dann, was passiert, wenn der Code den Wert nach Verbindungsöffnung ändert:

using Microsoft.Data.SqlClient;

string connectionString =
    Environment.GetEnvironmentVariable("SQL_CONNECTION_STRING")
    ?? throw new InvalidOperationException(
        "Set the SQL_CONNECTION_STRING environment variable.");

await using var connection = new SqlConnection(connectionString)
{
    RegisteredApplication = (RegisteredApplication)0xC001,
};

await connection.OpenAsync();
Console.WriteLine(
    $"Reported identifier: 0x{(ushort)connection.RegisteredApplication:X4}");

try
{
    connection.RegisteredApplication = RegisteredApplication.Unknown;
}
catch (InvalidOperationException ex)
{
    Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
}

Die Ausgabe lautet:

Reported identifier: 0xC001
InvalidOperationException: Not allowed to change the 'RegisteredApplication' property. The connection's current state is open.

Pooling-Effekte verstehen

Der Treiber meldet die Kennung erst, wenn eine neue physische Verbindung hergestellt wird. Mit aktiviertem Pooling:

  • Eine Verbindung, die eine gepoolte physische Verbindung wiederverwendet, meldet die Identität der Verbindung, die die physische Verbindung erzeugt hat, nicht ihre eigene.
  • Physische Verbindungen, die der Pool im Hintergrund öffnet, um die Anforderungen des Berichts Unknownzu erfüllenMin Pool Size.

Wenn eine Verbindungszeichenfolge mehrere Identitäten bedient, behandeln Sie den gemeldeten Wert als ungefähr. Deaktivieren Sie das Pooling für diese Verbindungszeichenfolge, wenn Sie eine exakte Attribution benötigen, und messen Sie zuerst die zusätzlichen Verbindungskosten.

SqlConnection.Clone Kopiert den RegisteredApplication Wert in die neue Verbindung.

Eine Verbindung nicht gleichzeitig gemeinsam nutzen.

SqlConnection, SqlCommand, , SqlDataReaderund SqlTransaction unterstützen keine gleichzeitige Nutzung durch mehrere Threads. Geben Sie jeder gleichzeitigen Operation ihre eigene Verbindung, und lassen Sie das Verbindungspooling für die Wiederverwendung der physischen Verbindungen sorgen.

Multiple Active Result Sets (MARS) erlaubt in unterstützten Szenarien mehrere aktive Batches auf einer Verbindung. Es macht SqlClient-Objekte nicht threadsicher und fügt Session- und Transaktionsregeln hinzu. Lass es deaktiviert, es sei denn, ein Vorgang benötigt es ausdrücklich.

Registrieren Sie kein offenes SqlConnection als Singleton in der Abhängigkeitsinjektion. Registrieren Sie den Verbindungszeichenfolge, ein unveränderliches Optionsobjekt oder eine Fabrik, die eine neue Verbindung erstellt.

Nutze Transaktionen bewusst

Eine lokale Transaktion ist ihrer Verbindung zugeordnet. Jeder Befehl in der Transaktion muss diese Verbindung verwenden und deren Transaction Eigenschaft festlegen.

await using var connection = new SqlConnection(connectionString);
await connection.OpenAsync(cancellationToken);

await using SqlTransaction transaction =
    (SqlTransaction)await connection.BeginTransactionAsync(cancellationToken);

using var command = new SqlCommand(sql, connection, transaction);
command.Parameters.Add(
    new SqlParameter("@value", SqlDbType.Int) { Value = value });
await command.ExecuteNonQueryAsync(cancellationToken);

await transaction.CommitAsync(cancellationToken);

Wenn die Operation vor dem Commit fehlschlägt, wird die Transaktion durch das Verwerfen zurückgerollt. Halten Sie die Transaktionen kurz. Mach keine Netzwerkaufrufe, keine Benutzerinteraktionen oder nicht zusammenhängende Berechnungen, während eine Datenbanktransaktion Sperren hält.

Wenn System.Transactions.Transaction.Current aktiv ist, werden Open und OpenAsync standardmäßig automatisch registriert. Legen Sie Enlist=false nur fest, wenn der Vorgang außerhalb der umgebenden Transaktion bleiben muss.

Messung einer logischen Verbindung

Setze StatisticsEnabled auf true, um Treiberstatistiken für ein SqlConnection-Objekt zu sammeln:

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

string connectionString =
    Environment.GetEnvironmentVariable("SQL_CONNECTION_STRING")
    ?? throw new InvalidOperationException(
        "Set the SQL_CONNECTION_STRING environment variable.");

await using var connection = new SqlConnection(connectionString)
{
    StatisticsEnabled = true,
};

await connection.OpenAsync();
connection.ResetStatistics();

await using var command = new SqlCommand("SELECT name FROM sys.databases;", connection);
await using (SqlDataReader reader = await command.ExecuteReaderAsync())
{
    while (await reader.ReadAsync())
    {
    }
}

IDictionary statistics = connection.RetrieveStatistics();
Console.WriteLine($"ServerRoundtrips: {statistics["ServerRoundtrips"]}");
Console.WriteLine($"SelectRows: {statistics["SelectRows"]}");
Console.WriteLine($"BytesReceived > 0: {(long)statistics["BytesReceived"]! > 0}");

Die Ausgabe zeigt einen Roundtrip. Die Anzahl der Zeilen hängt von der Anzahl der Datenbanken auf dem Server ab:

ServerRoundtrips: 1
SelectRows: 14
BytesReceived > 0: True

RetrieveStatistics gibt einen Schnappschuss zurück. ResetStatistics beginnt eine neue Messgrenze. Stellen Sie StatisticsEnabled auf false, um das Sammeln zu beenden. Die bisher gesammelten Werte sind weiterhin verfügbar. Statistiken werden pro Verbindungsobjekt erfasst und verursachen zusätzlichen Aufwand; aktivieren Sie sie daher nur für gezielte Fehlerdiagnosen statt für jede Anfrage in der Produktion.

Verarbeiten von Verbindungsfehlern

Fangen Sie SqlException an einer Stelle ab, an der der Fehler protokolliert, übersetzt oder ein erneuter Versuch ausgelöst werden kann. Aufzeichnung:

  • Number
  • State
  • Class
  • ClientConnectionId
  • Der Operationsname sowie konfigurierte Server- und Datenbankkennungen

Protokolliere nicht die Verbindungszeichenfolge, das Passwort, das Client-Secret oder das Zugriffstoken.

Löschen Sie eine defekte Verbindung. Der Pool entfernt ungültige physische Verbindungen, sobald sie erkannt werden. Wenn sich eine Zugangsdaten, ein Token, ein Zertifikat, ein DNS-Ziel oder ein Server geändert hat, korrigieren Sie die Konfiguration, bevor Sie es erneut versuchen.

Verwenden Sie beschränkte Wiederholungslogik nur für vorübergehende Ausfälle. Wiederholungsversuch bei der ersten Verbindungsöffnung, Wiederherstellung inaktiver Verbindungen und Befehlswiederholung sind verschiedene Mechanismen. Weitere Informationen finden Sie unter Konfigurierbare Wiederholungslogik sowie die Optionen zur Wiederherstellung der Verbindung in den Verbindungsoptionen.