Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
SqlConnectionoznacza jedno logiczne połączenie z SQL Server, Azure SQL Database, Azure SQL Managed Instance lub bazą danych SQL w Microsoft Fabric. Podczas otwierania obiektu uzyskiwane jest fizyczne połączenie z puli połączeń, jeśli jest ono dostępne. Zamknięcie lub utylizacja przywraca to fizyczne połączenie z pulą.
Używaj obiektów krótkotrwałych SqlConnection jako jednostek pracy. Nie trzymaj jednego globalnego połączenia otwartego dla aplikacji.
Prerequisites
- SDK .NET 10 lub nowszy obsługiwany zestaw SDK platformy .NET. SDK działa na Windows, Linux i macOS. Tworzy także wersję .NET Framework każdego przykładu, która działa tylko w systemie Windows i wymaga .NET Framework 4.8.1.
- Baza danych, do której możesz się połączyć w SQL Server, Azure SQL Database, Azure SQL Managed Instance lub w bazie danych SQL w Microsoft Fabric.
Tworzenie projektu
Microsoft.Data.SqlClient 7.0 i późniejsze wersje wymagają pakietu Microsoft.Data.SqlClient.Extensions.Azure dopasowanego do wersji dla trybów uwierzytelniania Microsoft Entra ID dostarczanych przez sterownik, takich jak Active Directory Default. Jeśli Twoja aplikacja używa tylko uwierzytelniania SQL lub uwierzytelniania zintegrowanego z Windows, możesz to pominąć.
W pustym folderze wykonaj następujące polecenia:
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
Przykłady w tym artykule odczytują parametry połączenia ze zmiennej środowiskowejSQL_CONNECTION_STRING. Ustaw to przed uruchomieniem próbki. Aby poznać formaty parametry połączenia, zobacz Connection strings.
Każda próbka poza próbką transakcji jest kompletnym programem. Aby uruchomić jeden, zastąp zawartość Program.cs nim, a następnie uruchom dotnet run. Przykładowa transakcja to fragment twojej własnej metody. Zakładka .NET wykorzystuje instrukcje najwyższego poziomu, niejawne dyrektywy using oraz await using. Zakładka .NET Framework używa C# 7.3, który nie ma await using, więc zamiast tego używa instrukcji using.
Otwórz połączenie
Wywołaj OpenAsync w kodzie asynchronicznym lub Open w kodzie synchronicznym. Zadeklaruj połączenie z await using lub using tak, aby zostało ono zwolnione, gdy kod opuści zakres, nawet gdy zostanie wyrzucony wyjątek.
W przypadku zintegrowanej uwierzytelniania w Windows, połączenie korzysta z tożsamości procesu. W przypadku aplikacji webowej lub usługi Windows jest to pula aplikacji lub konto usługowe, a nie użytkownik zalogowany. Więcej informacji można znaleźć w artykule Wybierz wzorzec uwierzytelniania.
Ten kompletny program otwiera połączenie, raportuje każdą zmianę stanu i odczytuje dwie właściwości serwera:
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)}");
}
Dla SQL Server 2025 Enterprise wyniki są następujące:
State: Closed to Open
Edition: Enterprise Edition: Core-based Licensing (64-bit)
EngineEdition: 3
State: Open to Closed
Ostatnia linia pojawia się, gdy połączenie zostanie zwolnione na końcu programu.
Zamknij i zwolnij połączenia
Zawsze zamykaj połączenie po jego użyciu, aby fizyczne połączenie wróciło do puli. Instrukcje using i await using zwalniają połączenie, gdy kod opuszcza blok, w tym gdy zostanie wyrzucony wyjątek nieobsługiwany. Możesz też zadzwonić Close lub Dispose bezpośrednio.
Close i Dispose mają taki sam wpływ na połączenie fizyczne.
Postępuj zgodnie z następującymi regułami:
- Pozbądź się czytników i poleceń przed połączeniem, które je posiada.
- Nie polegaj na odśmiecaniu pamięci, żeby przywrócić połączenie do puli. Połączenie, które wychodzi z zakresu bez zamknięcia, nie jest szybko zwracane, a w puli może zabraknąć połączeń.
- Nie wywołuj
CloselubDisposena połączeniu, czytniku ani żadnym innym zarządzanym obiekcie w finalizatorze klasy. Finalizer powinien uwalniać tylko niezarządzane zasoby, które jego klasa posiada bezpośrednio. Więcej informacji można znaleźć w artykule Garbage collection. - Po
Close, możesz ponownie wywołaćOpenna tym samym obiekcie. PoDispose, utwórz nowy obiekt.
Przy włączonym poolingu zamknięcie połączenia zwraca je do puli bez zamykania połączenia sieciowego. Pula połączeń resetuje stan sesji po ponownym użyciu połączenia. Więcej informacji można znaleźć w artykule o puli połączeń SQL Server.
Używanie asynchronicznych interfejsów API
Używaj wywołań asynchronicznych do pracy z bazą danych sieciowych na serwerach WWW, usługach, interfejsach użytkownika i pracownikach:
OpenAsync(cancellationToken)ExecuteNonQueryAsync(cancellationToken)ExecuteReaderAsync(cancellationToken)ExecuteScalarAsync(cancellationToken)ReadAsync(cancellationToken)
Nie rozpoczynaj kolejnej operacji na połączeniu, poleceniu lub czytniku przed zakończeniem bieżącej operacji asynchronicznej.
Zastosuj anulowanie i limity czasu
Przekaż sygnały CancellationToken dzwoniącego przez każde asynchroniczne wywołanie bazy danych. Anulowanie powoduje, że sterownik przerywa oczekujące zadania, ale ich zakończenie nie musi nastąpić natychmiast. Kontynuuj stosowanie ograniczonych limitów czasu połączeń i poleceń.
Te sterowanie ma osobne zakresy:
| Kontrola | Scope |
|---|---|
Connect Timeout |
Nawiązanie połączenia, czyli oczekiwanie na połączenie z puli |
SqlCommand.CommandTimeout |
Wykonanie jednego polecenia |
CancellationToken |
Anulowanie operacji asynchronicznej na żądanie dzwoniącego |
Przekroczenie limitu czasu lub anulowanie nie oznacza, że serwer cofnął operację. Używaj transakcji, gdy wiele zmian musi zostać zatwierdzonych lub wycofanych jako jedna całość, a decyzje o ponowieniu podejmuj na podstawie idempotencji operacji i wyniku transakcji.
Stan połączenia
Właściwość State zwraca migawkę z wyliczenia ConnectionState.
| State | Meaning |
|---|---|
Closed |
Logiczne połączenie nie jest otwarte. |
Connecting |
Trwa otwarta operacja. |
Open |
Logiczne połączenie jest otwarte. |
Wydarzenie StateChange uruchamia się, gdy zmienia się stan. Jego StateChangeEventArgs argument ma dwie własności:
-
OriginalStateto stan przed zmianą. -
CurrentStatejest stanem po zmianie.
Sterownik raportuje przejścia od Closed do Open oraz od Open do Closed, jak pokazano w przykładzie w Open a connection. Nie polegaj na obserwacji Executing, Fetching, ani Broken jako faz cyklu życia aplikacji.
Nie używaj State do kontroli stanu przed wykonaniem każdego polecenia. Sieć może ulec awarii po każdym sprawdzeniu. Wykonaj operację i obsłuż powstały wyjątek. Używaj StateChange do diagnostyki, a nie do koordynowania współbieżnej pracy.
Otrzymuj wiadomości informacyjne
Zdarzenie InfoMessage dostarcza komunikaty wysyłane przez serwer bez zgłaszania błędu. Wiadomości te obejmują PRINT sygnały wyjściowe, RAISERROR wiadomości o poziomie ważności 10 lub niższym oraz ostrzeżenia serwera. Błędy o stopniu 11 lub wyższym powodują zamiast tego zgłoszenie SqlException. Informacje o poziomach ważności można znaleźć w Poziomy ważności błędów aparatu bazy danych.
Procedura obsługi zdarzenia otrzymuje obiekt SqlInfoMessageEventArgs. Jego właściwość Errors zawiera zbiór obiektów SqlError. Każdy z nich zawiera numer wiadomości, tekst, klasę nasilenia, procedurę oraz numer linii.
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();
Dane wyjściowe to:
Message 0, severity 0: Starting the batch.
Message 50000, severity 0: Processed 42 rows.
Serwer zgłasza RAISERROR stopień nasilenia 10 jako klasę 0, więc SqlError.Class pokazuje 0 dla obu komunikatów.
Traktuj błędy użytkownika jako komunikaty informacyjne
Domyślnie sterownik zgłasza błąd o ważności 11 lub wyższej, rzucając wyjątek SqlException. Ten wyjątek nie oznacza, że serwer zatrzymał wsad. Wiele błędów, w tym RAISERROR o poziomie ważności 16, sprawia, że serwer wykonuje pozostałe instrukcje, więc późniejsze instrukcje mogą zmieniać dane nawet wtedy, gdy kod przechwytuje wyjątek. Aby otrzymywać te błędy jako komunikaty zamiast wyjątków, ustaw FireInfoMessageEventOnUserErrors na true. Następnie sterownik wysyła błędy o poziomie ważności 16 lub niższym do zdarzenia InfoMessage zamiast zgłaszać wyjątek. Ta cecha zmienia sposób, w jaki sterownik zgłasza błędy. Nie zmienia to, które instrukcje uruchamia serwer.
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());
}
Dane wyjściowe to:
Message 50000, severity 16: Row 7 failed validation.
The batch continued.
Błędy o poziomie ważności 17 lub wyższym nadal generują SqlException, niezależnie od tego ustawienia. Gdy włączasz tę właściwość, twoja procedura obsługi odpowiada za wykrywanie niepowodzeń. Kod, który oczekuje wyjątku dla każdego błędu, nie widzi żadnego wyjątku.
Zgłoś identyfikator aplikacji
Zaczynając od Microsoft. Data.SqlClient 7.1.0, framework, narzędzie lub inne oprogramowanie pośredniczące, może raportować, która aplikacja nawiązała połączenie. Ustaw RegisteredApplication na wartość RegisteredApplication przed otwarciem połączenia. Sterownik wysyła wartość do serwera raz, podczas logowania.
Postępuj zgodnie z następującymi regułami:
- Ustaw wartość za pomocą właściwości. Nie istnieje dla niego żadne słowo kluczowe parametru połączenia.
- Ustaw wartość przed otwarciem połączenia. Ustawienie go, gdy połączenie się otwiera lub jest otwarte, powoduje zgłoszenie wyjątku InvalidOperationException.
- Traktuj tę wartość jako telemetrię. Klient dostarcza wartość i może zgłosić dowolny identyfikator, więc serwer tego nie uwierzytelnia.
- Oczekuj, że połączenia z puli będą zgłaszać oryginalną tożsamość. Wartość nie jest częścią klucza puli. Połączenie serwowane z puli raportuje tożsamość logicznego połączenia, które spowodowało utworzenie fizycznego połączenia.
Caution
Nigdy nie używaj RegisteredApplication do autoryzacji ani do podejmowania jakichkolwiek innych decyzji dotyczących bezpieczeństwa. Każdy klient może podać dowolny identyfikator.
Wybierz identyfikator
Domyślna wartość to Unknown, czyli 0. Enumeracja ma podstawowy typ ushort, więc identyfikatory wahają się od 0 do 0xFFFF.
| Zakres | Używanie |
|---|---|
0x0001 do 0x7FFF |
Aplikacje zdefiniowane przez Microsoft. Nazwane członki wyliczenia korzystają z tego zakresu. |
0x8000 do 0xBFFF |
Zarezerwowane do użytku na niewielką skalę. |
0xC000 do 0xFFFF |
Do użytku publicznego i deweloperskiego. |
Nazwane elementy to AspNetCoreDistributedSqlServerCache, EntityFramework, SqlPackage, DurableTaskSqlServer, OrleansAdoNet, AzureFunctionsSqlExtension, DataApiBuilder, SqlToolsService, DataTierApplicationFramework, SqlManagementObjects, ManagementStudio, SemanticKernel oraz EntityFrameworkCore. Aby zgłosić błąd dla identyfikatora bez nazwanego członka, rzutuj liczbę na RegisteredApplication.
Typ RegisteredApplication i własność nie są zgodne z CLS, ponieważ wyliczanie używa ushort. Kod C# może z nich korzystać bezpośrednio.
Ustaw identyfikator
Program ten raportuje identyfikator z zakresu dewelopera i pokazuje, co się dzieje, gdy kod zmienia wartość po otwarciu połączenia:
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}");
}
Dane wyjściowe to:
Reported identifier: 0xC001
InvalidOperationException: Not allowed to change the 'RegisteredApplication' property. The connection's current state is open.
Poznaj efekty poolingu
Sterownik zgłasza identyfikator tylko wtedy, gdy tworzy nowe fizyczne połączenie. Przy włączonym poolingu:
- Połączenie, które ponownie korzysta z fizycznego połączenia z puli połączeń, raportuje tożsamość połączenia, które utworzyło to połączenie, a nie własne.
- Fizyczne połączenia, które pula otwiera w tle, aby obsłużyć raport
Min Pool SizeUnknown.
Jeśli jeden parametry połączenia obsługuje kilka tożsamości, należy traktować podaną wartość jako przybliżoną. Wyłącz pooling dla tego parametry połączenia, gdy potrzebujesz dokładnej atribucji, i najpierw zmierz dodatkowy koszt połączenia.
SqlConnection.Clone kopiuje wartość RegisteredApplication do nowego połączenia.
Nie udostępniaj połączenia jednocześnie
SqlConnection, SqlCommand, SqlDataReader, oraz SqlTransaction nie obsługują jednoczesnego użycia przez wiele wątków. Przypisz każdej współbieżnej operacji własne połączenie i pozwól, aby pula połączeń ponownie wykorzystywała połączenia fizyczne.
Multiple Active Result Sets (MARS) pozwala na wiele aktywnych partii na jednym połączeniu w obsługiwanych scenariuszach. Nie czyni obiektów SqlClient bezpiecznymi wątkowo, a dodatkowo dodaje reguły sesji i transakcji. Zostaw wyłączone, chyba że jedna operacja tego wymaga wprost.
Nie rejestruj otwartego SqlConnection jako singletona w wstrzyknięciu zależności. Zarejestruj parametry połączenia, niezmienny obiekt opcji lub fabrykę tworzącą nowe połączenie.
Używaj transakcji celowo
Transakcja lokalna jest przypisana do swojego połączenia. Każde polecenie w transakcji musi korzystać z tego połączenia i ustawić jego właściwość Transaction.
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);
Jeśli operacja nie powiedzie się przed zatwierdzeniem, zwolnienie transakcji powoduje jej wycofanie. Utrzymuj transakcje krótkie. Nie wykonuj wywołań sieciowych, interakcji z użytkownikami ani nie wykonuj niezwiązanych obliczeń, gdy transakcja w bazie danych jest zablokowana.
Gdy System.Transactions.Transaction.Current jest aktywne, Open i OpenAsync są domyślnie automatycznie rejestrowane. Ustaw Enlist=false tylko wtedy, gdy operacja musi być wykonywana poza transakcją nadrzędną.
Zmierz jedno połączenie logiczne
Ustaw StatisticsEnabled na true , aby zbierać statystyki kierowcy dla jednego SqlConnection obiektu:
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}");
Wyniki pokazują jeden przebieg w obie strony. Liczba wierszy zależy od liczby baz danych na serwerze:
ServerRoundtrips: 1
SelectRows: 14
BytesReceived > 0: True
RetrieveStatistics zwraca zrzut.
ResetStatistics rozpoczyna się nowa granica pomiarowa. Ustaw StatisticsEnabled na false, aby zatrzymać zbieranie. Zebrane dotychczas wartości pozostają dostępne. Statystyki dotyczą poszczególnych obiektów połączenia i powodują dodatkowy narzut, więc włączaj je tylko na potrzeby ukierunkowanej diagnostyki, a nie dla każdego żądania produkcyjnego.
Obsługa błędów połączeń
Złap SqlException na granicy, która może zarejestrować, przetłumaczyć lub ponownie spróbować niepowodzenia. Rekord:
NumberStateClassClientConnectionId- Nazwa operacji oraz skonfigurowane identyfikatory serwera i bazy danych
Nie rejestruj w dzienniku parametrów połączenia, hasła, wpisu tajnego klienta ani tokenu dostępu.
Pozbądź się zerwanego połączenia. Pula usuwa nieprawidłowe połączenia fizyczne, gdy je wykryje. Jeśli dane uwierzytelniające, token, certyfikat, cel DNS lub serwer się zmieniły, popraw konfigurację przed ponowną próbą.
Stosuj logikę ograniczonej liczby ponowień tylko w przypadku błędów przejściowych. Ponowna próba początkowego otwarcia, odzyskiwanie bezczynnego połączenia oraz ponawianie polecenia to różne mechanizmy. Więcej informacji można znaleźć w sekcji Konfigurowalna logika powtórek oraz informacje o opcjach odzyskiwania połączenia w opcjach połączenia.