Executar um comando

Use SqlCommand para executar instruções Transact-SQL (T-SQL) ou procedimentos armazenados. Um comando precisa de um arquivo aberto SqlConnection. Descarte o comando após o uso e descarte qualquer reader antes de reutilizar a conexão.

Os exemplos deste artigo presumem que uma conexão connection aberta chamada SqlConnection tem como destino o banco de dados de exemplo AdventureWorks.

Os exemplos de C# requerem using Microsoft.Data.SqlClient;. Exemplos que usam SqlDbType, CommandType, ou CommandBehavior também requerem using System.Data;.

Para configuração, autenticação e criptografia de conexão, veja Strings de conexão e Criptografia e validação de certificados.

Escolha um método de execução

Escolha o método que corresponde ao resultado esperado:

Método Usar quando Resultado
ExecuteNonQuery O comando altera dados ou esquema. O número de linhas afetadas. -1 pode ocorrer quando não há contagem disponível, inclusive quando SET NOCOUNT ON suprime contagens.
ExecuteScalar Você precisa de um valor. A primeira coluna da primeira linha, ou null quando o resultado não tem linhas. Um banco de dados NULL é retornado como Value.
ExecuteReader Você precisa de linhas, múltiplos conjuntos de resultados ou esquema. Um SqlDataReader.
ExecuteXmlReader O comando retorna XML na forma exigida por ExecuteXmlReader. Um XmlReader.

Cada método possui um equivalente assíncrono baseado em tarefas. Passe a CancellationToken quando o chamador precisar parar de esperar. Para comportamento de cancelamento e timeout, veja Programação assíncrona.

Executar um comando de texto

CommandType.Text é o padrão. Coloque a instrução em CommandText e passe valores pelos parâmetros:

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}");
}

Para tipos de parâmetros, tamanhos, valores nulos e direções, veja Configurar parâmetros. Não concatene valores não confiáveis a CommandText.

Executar um procedimento armazenado

Defina CommandType.StoredProcedure e use o nome do procedimento qualificado pelo esquema:

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 suporta CommandType.Text e CommandType.StoredProcedure. Definir CommandType.TableDirect gera ArgumentOutOfRangeException.

Verifique o que cada método retorna

Use ExecuteScalar quando você precisa de um valor:

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 retorna null quando o resultado não tem linhas e DBNull.Value quando o primeiro valor é um valor de banco de dados NULL.

Use ExecuteNonQuery para instruções que alteram dados. Este exemplo reverte sua alteração para que o AdventureWorks permaneça inalterado:

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 suprime a contagens de linhas afetadas, então ExecuteNonQuery pode retornar -1 mesmo quando uma instrução altera os dados.

Use ExecuteXmlReader para um FOR XML resultado:

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());

Use uma transação local

Quando uma conexão tem uma transação local ativa SqlTransaction, atribua essa transação a cada comando executado na conexão. Este exemplo reverte sua alteração para que o AdventureWorks permaneça inalterado:

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();

Ligue Commit em vez de Rollback apenas quando a aplicação aceitar a transação completa. Um comando que não faz referência à transação ativa lança InvalidOperationException. A execução também falha se a transação pertencer a outra conexão. Para limites de transação e rollback, veja Transações Locais.

Defina um tempo limite de comando

CommandTimeout limita a execução de comandos e as esperas cumulativas de leitura de rede em segundos. Um comando usa a configuração da Command Timeout conexão quando uma está configurada. Caso contrário, o padrão é 30 segundos. Um valor de 0 não tem limite de tempo. Valores negativos geram ArgumentException.

Quando o tempo limite expira, o comando lança SqlException com Number definido para -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 limita o estabelecimento de conexão e as esperas no pool de conexões. CommandTimeout começa após o início da execução do comando. Para mais informações, veja Opções de conexão.

Use um tempo limite finito e um token de cancelamento juntos. O timeout estabelece um limite para a operação do banco de dados. Cancelar o token pode devolver o controle ao chamador antes do timeout expirar. Não presuma que cancelamento e timeout produzem a mesma exceção.

Reutilizar e preparar comandos

Adicione um parâmetro uma vez e então mude seu valor após o término de cada execução:

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)}");
    }
}

Chame Prepare apenas quando as medições justificarem. Prepare não prepara procedimentos armazenados ou comandos de texto sem parâmetros. Para texto parametrizado, defina metadados completos como Size, Scale e Prepare em cada parâmetro antes de chamar Precision.

Comandos de solução de problemas

Capture SqlException durante a operação do banco de dados e inspecione sua coleção Errors quando o servidor devolver mais de um erro. Para erros de comando, avisos e mensagens informativas, consulte Tratar erros e mensagens.

Use contadores de diagnóstico, rastreamento de fontes de eventos e o guia de solução de problemas do SqlClient quando um erro exigir mais contexto. Aplique lógica de retentativa configurável somente quando repetir o comando for seguro.