Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
SqlConnectionreprésente une connexion logique vers SQL Server, Azure SQL Database, Azure SQL Managed Instance, ou SQL Database dans Microsoft Fabric. L’ouverture de l’objet récupère une connexion physique dans le pool de connexions lorsqu’une connexion est disponible. Sa fermeture ou sa libération restitue cette connexion physique au pool.
Utilisez des objets à durée SqlConnection de vie éphémère pour les unités de travail. Ne laissez pas une seule connexion globale ouverte pour l’application.
Prerequisites
- Le SDK .NET 10 ou un SDK .NET ultérieur pris en charge. Le SDK fonctionne sous Windows, Linux et macOS. Il construit également la version .NET Framework de chaque exemple, qui fonctionne uniquement sous Windows et nécessite .NET Framework 4.8.1.
- Une base de données à laquelle vous pouvez vous connecter dans SQL Server, Azure SQL Database, Azure SQL Managed Instance, ou une base de données SQL dans Microsoft Fabric.
Créer le projet
Microsoft. Data.SqlClient 7.0 et versions ultérieures nécessitent le package adapté Microsoft.Data.SqlClient.Extensions.Azure à la version pour les modes d’authentification Microsoft Entra ID fournis par le pilote, tels que Active Directory Default. Si votre application utilise uniquement l’authentification SQL ou l’authentification intégrée Windows, vous pouvez l’omettre.
Dans un dossier vide, exécutez ces commandes :
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
Les exemples de cet article lisent la chaîne de connexion à partir de la variable d’environnement SQL_CONNECTION_STRING. Réglez-le avant d’exécuter un échantillon. Pour les formats de chaîne de connexion, voir Chaînes de connexion.
Chaque échantillon, sauf celui de transaction, est un programme complet. Pour en faire tourner un, remplacez le contenu de Program.cs par celui-ci, puis exécutez dotnet run. L’exemple de transaction est un fragment pour votre méthode. L’onglet .NET utilise des instructions de premier niveau, des directives implicites using, et des usings implicites await using. L'onglet .NET Framework utilise C# 7.3, qui ne dispose pas de await using, donc il utilise des instructions using à la place.
Ouvre une connexion
Appelez OpenAsync dans du code asynchrone ou Open dans du code synchrone. Déclarez la connexion avec await using ou using ; ainsi elle est libérée lorsque le code quitte la portée, même lorsqu’une exception est lancée.
Avec l’authentification intégrée Windows, la connexion utilise l’identité du processus. Pour une application web ou un service Windows, c'est le pool d'applications ou le compte de service, pas l'utilisateur connecté. Pour plus d’informations, voir Choisir une méthode d’authentification.
Ce programme complet ouvre une connexion, rapporte chaque changement d’état et lit deux propriétés de serveur :
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)}");
}
Comparé à SQL Server 2025 édition Entreprise, la sortie est la suivante :
State: Closed to Open
Edition: Enterprise Edition: Core-based Licensing (64-bit)
EngineEdition: 3
State: Open to Closed
La dernière ligne apparaît lorsque la connexion est libérée à la fin du programme.
Fermer et libérer les connexions
Fermez toujours une connexion lorsque vous avez fini de l’utiliser pour que la connexion physique revienne dans le pool. Les using instructions et await using ferment la connexion lorsque le code sort du bloc, y compris lorsqu’une exception non traitée est levée. Vous pouvez aussi appeler Close ou Dispose directement appeler.
Close et Dispose ont le même effet sur la connexion physique.
Suivez ces règles :
- Libérez les lecteurs et les commandes avant la connexion à laquelle ils sont associés.
- Ne comptez pas sur le ramasse-miettes pour renvoyer une connexion au pool de connexions. Une connexion qui sort de la portée sans être fermée n’est pas renvoyée rapidement, et le pool peut manquer de connexions.
- N’appelez pas
CloseniDisposesur une connexion, un lecteur de données ou tout autre objet géré dans un finaliseur de classe. Un finaliseur ne devrait libérer que les ressources non gérées que sa classe possède directement. Pour plus d’informations, voir Ramasse-miettes. - Après
Close, vous pouvez appelerOpenà nouveau sur le même objet. AprèsDispose, crée un nouvel objet.
Avec le pooling activé, fermer une connexion la renvoie au pool sans fermer la connexion réseau. Le pool réinitialise l’état de la session lorsque la connexion est réutilisée. Pour plus d’informations, voir le pooling de connexions SQL Server.
Utiliser des API asynchrones
Utilisez des appels asynchrones pour le travail de base de données en réseau dans les serveurs web, les services, les interfaces utilisateur et les travailleurs :
OpenAsync(cancellationToken)ExecuteNonQueryAsync(cancellationToken)ExecuteReaderAsync(cancellationToken)ExecuteScalarAsync(cancellationToken)ReadAsync(cancellationToken)
Ne commencez pas une autre opération sur une connexion, une commande ou un lecteur avant la fin de l’opération asynchrone en cours.
Appliquer les annulations et les délais d’expiration
Transmettez le CancellationToken de l'appelant à chaque appel asynchrone à la base de données. L’annulation demande au pilote d’arrêter le travail en attente, mais l’achèvement n’est pas garanti immédiat. Continuez à utiliser des délais d’attente de connexion et de commande bornés.
Ces contrôles ont des péripéties distinctes :
| Contrôle | Scope |
|---|---|
Connect Timeout |
Établissement de connexion, ou attente pour une connexion issue du pool |
SqlCommand.CommandTimeout |
Exécution d’une commande |
CancellationToken |
Annulation d’une opération asynchrone à demande de l’appelant |
Un délai d’attente ou une annulation ne prouve pas que le serveur a annulé une opération. Utilisez une transaction lorsque plusieurs modifications doivent être validées ou annulées comme une seule unité, et prenez des décisions de nouvelle tentative en fonction de l’idempotence de l’opération et du résultat de la transaction.
Suivi de l’état de la connexion
La State propriété renvoie un instantané issu de l’énumération ConnectionState .
| State | Meaning |
|---|---|
Closed |
La connexion logique n’est pas ouverte. |
Connecting |
Une opération ouverte est en cours. |
Open |
La connexion logique est ouverte. |
L’événement StateChange se déclenche lorsque l’état change. Son StateChangeEventArgs argument possède deux propriétés :
-
OriginalStateest l’état avant le changement. -
CurrentStateest l’état après modification.
Le pilote rapporte les transitions de Closed à Open et de Open à Closed, comme l’indique l’exemple dans Ouvrir une connexion. Ne considérez pas Executing, Fetching ou Broken comme des phases du cycle de vie de l’application.
Ne l’utilisez State pas comme contrôle de santé avant chaque commande. Le réseau peut tomber en panne après n’importe quel contrôle. Exécutez l’opération et gérez l’exception qui en résulte. Utilisez StateChange pour le diagnostic, pas pour coordonner le travail concurrent.
Recevoir des messages d’information
L’événement InfoMessage transmet des messages envoyés par le serveur sans générer d’erreur. Ces messages incluent PRINT la sortie, RAISERROR les messages de gravité de 10 ou moins, ainsi que les avertissements serveur. Les erreurs d’une gravité de 11 ou plus déclenchent plutôt une SqlException. Pour les niveaux de gravité, voir Moteur de base de données error severities.
Le gestionnaire d’événements reçoit un objet SqlInfoMessageEventArgs. Sa propriété Errors contient une collection d’objets SqlError. Chacune inclut le numéro du message, le texte, la classe de gravité, la procédure et le numéro de ligne.
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();
La sortie est la suivante :
Message 0, severity 0: Starting the batch.
Message 50000, severity 0: Processed 42 rows.
Le serveur indique une RAISERROR sévérité de 10 comme classe 0, donc SqlError.Class affiche 0 pour les deux messages.
Gérer les erreurs utilisateur sous forme de messages informatifs
Par défaut, le pilote signale une erreur de gravité de 11 ou plus en lançant un SqlException. Cette exception ne signifie pas que le serveur a arrêté le lot. De nombreuses erreurs, y compris RAISERROR ayant un niveau de gravité de 16, permettent au serveur d’exécuter les instructions restantes, de sorte que les instructions ultérieures peuvent modifier les données même lorsque votre code détecte l’exception. Pour recevoir ces erreurs sous forme de messages au lieu d’exceptions, définissez FireInfoMessageEventOnUserErrors sur true. Le driver envoie alors des erreurs d’une gravité de 16 ou moins à l’event InfoMessage au lieu de lever une exception. Cette propriété modifie la manière dont le pilote signale les erreurs. Cela ne change pas les instructions que le serveur exécute.
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());
}
La sortie est la suivante :
Message 50000, severity 16: Row 7 failed validation.
The batch continued.
Les erreurs de gravité de 17 ou plus produisent toujours un SqlException, quel que soit ce réglage. Lorsque vous activez cette propriété, votre gestionnaire d’événements est responsable de détecter les défaillances. Le code qui attend une exception pour chaque erreur n’en voit pas.
Déclarez l’identité de l’application
À partir de Microsoft.Data.SqlClient 7.1.0, un framework, un outil ou autre middleware, peut indiquer quelle application a ouvert une connexion. Définissez RegisteredApplication sur une valeur RegisteredApplication avant d’ouvrir la connexion. Le pilote logiciel envoie la valeur au serveur une fois, lors de la connexion.
Suivez ces règles :
- Fixez la valeur via la propriété. Il n’existe aucun mot-clé de chaîne de connexion correspondant.
- Définissez la valeur avant d’ouvrir la connexion. Le régler alors que la connexion est en cours d’ouverture ou ouverte génère une exception InvalidOperationException.
- Considérez la valeur comme de la télémétrie. Le client fournit la valeur et peut fournir n’importe quel identifiant, donc le serveur ne l’authentifie pas.
- Attendez-vous à ce que les connexions mises en pool renvoient l’identité d’origine. La valeur ne fait pas partie de la clé du pool. Une connexion servie depuis le pool rapporte l’identité de la connexion logique qui a causé la création de la connexion physique.
Avertissement
Ne jamais utiliser RegisteredApplication pour l’autorisation ou toute autre décision de sécurité. N’importe quel client peut signaler n’importe quel identifiant.
Choisissez un identifiant
La valeur par défaut est Unknown, qui est 0. L’énumération possède un type sous-jacent de ushort, donc les identifiants vont de 0 jusqu’à 0xFFFF.
| Gamme | Utiliser |
|---|---|
0x0001 à 0x7FFF |
Applications définies par Microsoft. Les membres d’énumération nommés utilisent cet intervalle. |
0x8000 à 0xBFFF |
Réservé à un usage à petite échelle. |
0xC000 à 0xFFFF |
Usage public et développeur. |
Les membres nommés sont EntityFrameworkCore, SemanticKernel, ManagementStudio, SqlManagementObjects, DataTierApplicationFramework, SqlToolsService, AspNetCoreDistributedSqlServerCache, OrleansAdoNet, AzureFunctionsSqlExtension, DurableTaskSqlServer, EntityFramework, SqlPackage et DataApiBuilder. Pour signaler un identifiant qui n’a pas de membre nommé, convertissez le nombre en RegisteredApplication.
Le type RegisteredApplication et la propriété ne sont pas conformes à CLS car l’énumération utilise ushort. Le code C# peut les utiliser directement.
Définir l’identifiant
Ce programme rapporte un identifiant de la plage de développeurs puis montre ce qui se passe lorsque le code modifie la valeur après l’ouverture de la connexion :
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}");
}
La sortie est la suivante :
Reported identifier: 0xC001
InvalidOperationException: Not allowed to change the 'RegisteredApplication' property. The connection's current state is open.
Comprendre les effets de la mise en commun
Le pilote signale l’identifiant uniquement lorsqu’il crée une nouvelle connexion physique. Lorsque le pooling est activé :
- Une connexion qui réutilise une connexion physique regroupée rapporte l’identité de la connexion qui a créé la connexion physique, et non la sienne.
- Connexions physiques que le pool ouvre en arrière-plan pour répondre à
Min Pool SizerapportUnknown.
Si une chaîne de connexion sert plusieurs identités, considérez la valeur rapportée comme approximative. Désactivez le pooling de connexions pour cette chaîne de connexion lorsque vous avez besoin d’une attribution exacte, et mesurez d’abord le coût supplémentaire de connexion.
SqlConnection.Clone copie la RegisteredApplication valeur dans la nouvelle connexion.
Ne partagez pas de connexion en même temps
SqlConnection, SqlCommand, SqlDataReader, et SqlTransaction ne supportent pas l’utilisation simultanée par plusieurs threads. Attribuez à chaque opération concurrente sa propre connexion et laissez le pool de connexions réutiliser les connexions physiques.
Multiple Active Result Sets (MARS) permet plusieurs lots actifs sur une même connexion dans les scénarios pris en charge. Cela ne rend pas les objets SqlClient sûrs en thread, et ajoute des règles de session et de transaction. Laissez-le désactivé sauf si une opération en a spécifiquement besoin.
N’enregistrez pas un type ouvert SqlConnection comme singleton dans l’injection de dépendances. Enregistrez la chaîne de connexion, un objet options immuable, ou une usine qui crée une nouvelle connexion.
Utilisez les transactions de manière délibérée
Une transaction locale appartient à sa connexion. Chaque commande de la transaction doit utiliser cette connexion et définir sa Transaction propriété.
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);
Si l’opération échoue avant le commit, libérer la transaction provoque son annulation. Gardez les transactions courtes. Ne faites pas d’appels réseau, d’interactions utilisateur ou de calculs sans lien tant qu’une transaction de base de données détient des verrous.
Lorsque System.Transactions.Transaction.Current est actif, Open et OpenAsync s’inscrivent automatiquement par défaut. Défini Enlist=false uniquement lorsque l’opération doit rester en dehors de la transaction ambiante.
Mesurez une connexion logique
Définissez StatisticsEnabled sur true pour collecter des statistiques de pilote pour un objet SqlConnection :
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}");
Le résultat indique un cycle aller-retour. Le nombre de lignes dépend du nombre de bases de données sur le serveur :
ServerRoundtrips: 1
SelectRows: 14
BytesReceived > 0: True
RetrieveStatistics renvoie un instantané.
ResetStatistics démarre une nouvelle délimitation de mesure. Réglez StatisticsEnabled sur false pour arrêter la collecte. Les valeurs collectées jusqu’à présent restent disponibles. Les statistiques sont par objet de connexion et ajoutent de la surcharge, donc activez-les pour un diagnostic ciblé plutôt que pour chaque demande de production.
Gérer les échecs de connexion
Interceptez SqlException à un niveau permettant de journaliser, de traduire ou de réessayer après l’échec. Enregistrer:
NumberStateClassClientConnectionId- Le nom de l’opération et les identifiants de serveur et de base de données configurés
Ne consignez pas la chaîne de connexion, le mot de passe, le secret client ou le jeton d'accès.
Éliminez une connexion cassée. Le pool supprime les connexions physiques invalides lorsqu’il les détecte. Si un identifiant, un jeton, un certificat, une cible DNS ou un serveur a changé, corrigez la configuration avant de réessayer.
Utilisez la logique de réessai bornée uniquement pour les défaillances transitoires. La réouverture initiale, la récupération de connexion inactive et la réévaluation par commande sont des mécanismes différents. Pour plus d’informations, voir Logique de réessayage configurable et les options de récupération de connexion dans Options de connexion.