Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Используйте SqlCommand для выполнения инструкций Transact-SQL (T-SQL) или хранимых процедур. Команда нуждается в открытом SqlConnection. Освободите команду после использования и освободите любой объект чтения, прежде чем использовать соединение повторно.
В примерах этой статьи предполагается, что открытый SqlConnection с именем connection нацелен на образцовую базу данных AdventureWorks.
Примеры C# требуют using Microsoft.Data.SqlClient;. Примеры, использующие SqlDbType, CommandType или CommandBehavior, также требуют using System.Data;.
Для настройки соединения, аутентификации и шифрования см. Строки соединения и Шифрование и проверка сертификатов.
Выберите метод выполнения
Выберите метод, который соответствует ожидаемому результату:
| Метод | Используйте, если | Результат |
|---|---|---|
| ExecuteNonQuery | Команда меняет данные или схему. | Количество затронутых строк.
-1 может возникать при отсутствии подсчёта, в том числе когда SET NOCOUNT ON подавляет подсчёт. |
| ExecuteScalar | Вам нужно одно значение. | Первый столбец первой строки, или null когда в результате нет строк. База данных NULL возвращается как Value. |
| ExecuteReader | Нужны строки, несколько наборов результатов или схема. | А SqlDataReader. |
| ExecuteXmlReader | Команда возвращает XML в форме, необходимой для ExecuteXmlReader. |
Объект XmlReader. |
Каждый метод имеет асинхронный аналог, основанный на задачах. Передайте CancellationToken, когда вызывающему коду нужно перестать ждать. Сведения о поведении при отмене и тайм-ауте см. в разделе асинхронное программирование.
Выполнить текстовую команду
CommandType.Text — значение по умолчанию. Вставьте оператор в CommandText и передайте значения через параметры:
const string sql = """
SELECT ProductID, Name, ListPrice
FROM Production.Product
WHERE ProductSubcategoryID = @subcategoryId
ORDER BY ListPrice DESC;
""";
using var command = new SqlCommand(sql, connection);
command.Parameters.Add("@subcategoryId", SqlDbType.Int).Value = 2;
using SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
Console.WriteLine(
$"{reader.GetInt32(0)} | {reader.GetString(1)} | {reader.GetDecimal(2):C}");
}
Сведения о типах параметров, размерах, нулевых значениях и направлениях см. в разделе Настройка параметров. Не конкатенируйте недоверенные значения с CommandText.
Выполнение хранимой процедуры
Установите CommandType.StoredProcedure и используйте имя процедуры, квалифицированное по схеме:
using var command = new SqlCommand(
"dbo.uspGetEmployeeManagers",
connection);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add(
"@BusinessEntityID",
SqlDbType.Int).Value = 6;
using SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
Console.WriteLine(
$"{reader.GetString(2)} {reader.GetString(3)}");
}
Microsoft. Data.SqlClient поддерживает CommandType.Text и CommandType.StoredProcedure. Параметр CommandType.TableDirect вызывает ArgumentOutOfRangeException.
Проверьте, что даёт каждый метод
Используйте ExecuteScalar тогда, когда нужно одно значение:
using var command = new SqlCommand(
"SELECT COUNT(*) FROM Production.Product WHERE SellEndDate IS NULL;",
connection);
int currentProductCount = (int)command.ExecuteScalar()!;
Console.WriteLine($"Current products: {currentProductCount}");
ExecuteScalar возвращает null, когда в результате нет строк, и DBNull.Value, когда первое значение — значение базы данных NULL.
Используйте ExecuteNonQuery для операторов, изменяющих данные. Этот пример откатывает изменения, так что AdventureWorks остаётся без изменений:
using SqlTransaction transaction = connection.BeginTransaction();
using var command = new SqlCommand("""
UPDATE Sales.SalesOrderHeader
SET Status = @status
WHERE SalesOrderID = @salesOrderId;
""", connection, transaction);
command.Parameters.Add("@status", SqlDbType.TinyInt).Value = 2;
command.Parameters.Add("@salesOrderId", SqlDbType.Int).Value = 43659;
int rowsAffected = command.ExecuteNonQuery();
Console.WriteLine($"Rows affected: {rowsAffected}");
transaction.Rollback();
SET NOCOUNT ON подавляет количество затронутых строк, поэтому ExecuteNonQuery может возвращать -1 даже когда оператор изменяет данные.
Используйте ExecuteXmlReader для получения результата FOR XML:
using var command = new SqlCommand("""
SELECT TOP (3) ProductID, Name
FROM Production.Product
ORDER BY ProductID
FOR XML PATH('Product'), ROOT('Products');
""", connection);
using System.Xml.XmlReader reader = command.ExecuteXmlReader();
reader.MoveToContent();
Console.WriteLine(reader.ReadOuterXml());
Используйте локальную транзакцию
Когда соединение имеет активное локальное SqlTransaction, назначайте эту транзакцию каждой команде, выполняемой на соединении. Этот пример откатывает изменения, так что AdventureWorks остаётся без изменений:
using SqlTransaction transaction = connection.BeginTransaction();
using var command = new SqlCommand("""
UPDATE Sales.SalesOrderHeader
SET Status = @status
WHERE SalesOrderID = @salesOrderId;
""", connection, transaction);
command.Parameters.Add("@status", SqlDbType.TinyInt).Value = 2;
command.Parameters.Add("@salesOrderId", SqlDbType.Int).Value = 43659;
command.ExecuteNonQuery();
transaction.Rollback();
Вызывайте Commit вместо Rollback только тогда, когда приложение принимает всю транзакцию. Команда, не ссылающаяся на активную транзакцию, вызывает InvalidOperationException. Выполнение также завершается с ошибкой, если транзакция принадлежит другому соединению. Для границ транзакций и отката см. раздел «Локальные транзакции».
Задайте тайм-аут команды
CommandTimeout ограничивает выполнение команд и накопленное ожидание чтения из сети в секундах. Команда использует параметр подключения Command Timeout, если он задан. В противном случае по умолчанию — 30 секунд. Значение 0 не имеет ограничения по времени. При отрицательных значениях возникает ArgumentException.
Когда тайм-аут истекает, команда вызывает исключение SqlException с параметром Number, установленным в значение -2:
using var command = new SqlCommand(
"WAITFOR DELAY '00:00:05';",
connection)
{
CommandTimeout = 1
};
try
{
command.ExecuteNonQuery();
}
catch (SqlException exception) when (exception.Number == -2)
{
Console.WriteLine("The command timed out.");
}
Connect Timeout Ограничение установления соединений и ожидания в пуле соединений.
CommandTimeout начинается после начала выполнения команды. Для получения дополнительной информации смотрите раздел «Параметры подключения».
Используйте ограниченный тайм-аут и токен отмены вместе. Тайм-аут устанавливает лимит на операцию с базой данных. Отмена токена может вернуть контроль вызывающему до истечения тайм-аута. Не думайте, что отмена и тайм-аут приводят к одному и тому же исключению.
Повторное использование и подготовка команд
Добавьте параметр один раз, а затем изменяйте его значение после завершения каждого выполнения:
using var command = new SqlCommand("""
SELECT TOP (1) Name, ListPrice
FROM Production.Product
WHERE ProductSubcategoryID = @subcategoryId
ORDER BY ListPrice DESC;
""", connection);
SqlParameter subcategoryId = command.Parameters.Add(
"@subcategoryId",
SqlDbType.Int);
foreach (int id in new[] { 1, 2, 3 })
{
subcategoryId.Value = id;
using SqlDataReader reader = command.ExecuteReader();
if (reader.Read())
{
Console.WriteLine($"{id}: {reader.GetString(0)}");
}
}
Звоните Prepare только тогда, когда измерения это оправдывают.
Prepare не готовит хранимые процедуры или текстовые команды без параметров. Для параметризованного текста установите полные метаданные, такие как Size, Scale и Prepare, для каждого параметра перед вызовом Precision.
Команды устранения неполадок
Перехватите SqlException при выполнении операции с базой данных и проверьте её коллекцию Errors, если сервер возвращает более одной ошибки. Для ошибок команд, предупреждений и информационных сообщений см. раздел «Обработка ошибок и сообщений».
Используйте диагностические счётчики, отслеживание источников событий и руководство по устранению неполадок SqlClient , когда ошибка требует дополнительного контекста. Применяйте настраиваемую логику повтора только тогда, когда повторение команды безопасно.