Подключитесь к источнику данных с помощью Microsoft. Data.SqlClient

SqlConnectionпредставляет собой одно логическое соединение с SQL Server, Azure SQL или другой поддерживаемой с SQL Server конечной точкой. При открытии объекта ему выделяется физическое соединение из пула соединений, если оно доступно. Закрытие или утилизация возвращает физическое соединение с пулом.

Используйте объекты SqlConnection с коротким сроком жизни для отдельных единиц работы. Не оставляйте открытым одно глобальное соединение для приложения.

Постройте конфигурацию соединения

Загрузите строку подключения из системы конфигурации приложения. Используйте SqlConnectionStringBuilder тогда, когда код требует проверки или добавления настроек:

string configuredConnectionString =
    configuration.GetConnectionString("Orders")
    ?? throw new InvalidOperationException(
        "Connection string 'Orders' wasn't configured.");

var builder = new SqlConnectionStringBuilder(configuredConnectionString)
{
    ApplicationName = "Orders.Api",
};

Создайте SqlConnection из builder.ConnectionString. Не объединяйте пользовательский ввод в строку. Для паттернов аутентификации, безопасного хранения и синтаксиса см. раздел «Строки соединения».

Открытие и закрытие соединений

Вызовите Open в синхронном коде или OpenAsync в асинхронном коде. Откройте новое логическое соединение для каждой независимой операции:

public static async Task<string?> LoadOrderStatusAsync(
    string connectionString,
    int orderId,
    CancellationToken cancellationToken)
{
    await using var connection = new SqlConnection(connectionString);
    await connection.OpenAsync(cancellationToken);

    const string sql = """
        SELECT Status
        FROM Sales.Orders
        WHERE OrderId = @orderId;
        """;

    using var command =
        new SqlCommand(sql, connection) { CommandTimeout = 30 };
    command.Parameters.Add(
        new SqlParameter("@orderId", SqlDbType.Int) { Value = orderId });

    object? value =
        await command.ExecuteScalarAsync(cancellationToken);
    return value is null or DBNull ? null : (string)value;
}

Инструкция await using закрывает соединение в случае успешного выполнения, ошибки или отмены. При включённом пуле соединений освобождение объекта обычно приводит к сбросу и возврату физического соединения в пул вместо закрытия его сетевого сокета.

Избавляйтесь от считывателей и команд до соединения, которое ими владеет. Не полагайтесь на сборку мусора или финалайзер для восстановления соединений с пулом.

Использование асинхронных API

Используйте асинхронные вызовы для работы с сетевыми базами данных в веб-серверах, сервисах, пользовательских интерфейсах и рабочих:

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

Тебе не нужно Asynchronous Processing=true. Microsoft.Data.SqlClient 4.0 и более поздние версии не поддерживают это ключевое слово строки подключения.

Не начинайте новую операцию на соединении, команде или считывателе до завершения текущей асинхронной операции.

Применение отмены и тайм-аута

Передайте звонящим CancellationToken через каждый асинхронный вызов базы данных. Отмена требует от поставщика прекратить невыполненные работы, но завершение не гарантировано немедленно. Продолжайте использовать ограниченное соединение и тайм-ауты команд.

Эти органы управления имеют отдельные задачи:

Управление Объем
Connect Timeout Установление соединения или ожидание соединения из пула
SqlCommand.CommandTimeout Одно выполнение команды
CancellationToken Отмена асинхронной операции по запросу звонящего

Тайм-аут или отмена не доказывают, что сервер откатил операцию назад. Используйте транзакцию, когда несколько изменений должны быть зафиксированы или отменены как одно целое, и принимайте решение о повторной попытке на основе идемпотентности операции и результата транзакции.

Понимание состояния соединения

Свойство State возвращает снимок перечисления ConnectionState.

State Значение
Closed Логическая связь не открыта.
Connecting Открытая операция идёт.
Open Логическая связь открыта.

Не используйте State для проверки работоспособности перед каждой командой. Сеть может выходить из строя после любой проверки. Выполните операцию и обработайте возникшее исключение.

Драйвер обычно сообщает о переходах из закрытого состояния в открытое и из открытого в закрытое. Не рассматривайте Executing, Fetching или Broken как фазы жизненного цикла приложения.

Событие StateChange сообщает о переходах состояний. Событие InfoMessage сигнализирует об информационных сообщениях и предупреждениях сервера, которые не приводят к возникновению исключений. Используйте эти события для диагностики, а не для координации параллельной работы.

Не делитесь соединением одновременно

SqlConnection, SqlCommand, SqlDataReader, и SqlTransaction не поддерживают одновременное использование несколькими потоками. Дайте каждой одновременной операции отдельное соединение и позвольте пулу соединений повторно использовать физические соединения.

Несколько активных наборов результатов (MARS) позволяют использовать несколько активных пакетов на одном соединении в поддерживаемых сценариях. Это не делает объекты SqlClient потокобезопасными и добавляет правила для сеансов и транзакций. Оставьте его отключённым, если только одна из операций не требует этого конкретно.

Не регистрируйте открытый универсальный тип SqlConnection как синглтон при внедрении зависимостей. Зарегистрируйте строку подключения, неизменяемый объект параметров или фабрику, создающую новое соединение.

Используйте транзакции осознанно

Локальная транзакция принадлежит своему соединению. Каждая команда в транзакции должна использовать это соединение и задавать его 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);

Если операция проваливается до CommitAsync, избавление от транзакции откатывает её обратно. Держите транзакции короткими. Не выполняйте сетевые звонки, не взаимодействуйте с пользователем или не связанные вычисления, пока транзакция базы данных заблокирована.

Когда System.Transactions.Transaction.Current активен, OpenAsync и Open автоматически подключаются по умолчанию. Устанавливать Enlist=false только тогда, когда операция должна оставаться вне окружающей транзакции.

Измерьте одну логическую связь

Установите для StatisticsEnabled значение true, чтобы собирать статистику поставщика для одного объекта SqlConnection:

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

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

using var command = new SqlCommand(sql, connection);
await command.ExecuteNonQueryAsync(cancellationToken);

System.Collections.IDictionary statistics =
    connection.RetrieveStatistics();
long roundTrips =
    Convert.ToInt64(statistics["ServerRoundtrips"]);

RetrieveStatistics возвращает снимок. ResetStatistics обозначает начало новой области измерения. Установите StatisticsEnabled=false, чтобы остановить сбор; уже собранные значения останутся доступными. Статистика ведётся по объекту соединения и добавляет накладные расходы, поэтому включите их для целенаправленной диагностики, а не для каждого производственного запроса.

Для измерений пула и соединений по всему процессу используйте диагностические счётчики SqlClient.

Обработка неудачных подключений

Перехватывайте SqlException на уровне границы, которая может зарегистрировать, преобразовать сбой или повторить попытку. Записи:

  • Number
  • State
  • Class
  • ClientConnectionId
  • Имя операции и настроенные идентификаторы сервера и базы данных

Не записывайте строка подключения, пароль, секрет клиента или токен доступа.

Избавьтесь от сломанного соединения. Пул удаляет недействительные физические соединения при их обнаружении. Если изменились учетные данные, токен, сертификат, цель DNS или сервер, исправьте конфигурацию перед повторной попыткой.

Используйте ограниченную логику повторных попыток только для временных сбоев. Повторная попытка при первом открытии, восстановление в режиме простоя соединения и повторная попытка команды — это разные механизмы. См. Конфигурируемую логику повторного повтора.