Executar um comando

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

Os exemplos neste artigo assumem que uma connection aberta com o nome SqlConnection tem como alvo a base de dados de exemplo do AdventureWorks.

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

Para a configuração, autenticação e encriptação da ligação, consulte Cadeias de ligação e Encriptação e validação de certificados.

Escolha um método de execução

Escolha o método que corresponde ao resultado esperado:

Method Utilizar quando Result
ExecuteNonQuery O comando altera dados ou esquema. O número de linhas afetadas. -1 pode ocorrer quando não há contagem disponível, incluindo quando SET NOCOUNT ON suprime contagens.
ExecuteScalar Precisas de um valor. A primeira coluna da primeira linha, ou null quando o resultado não tem linhas. Uma base de dados NULL é devolvida como Value.
ExecuteReader Precisas de linhas, múltiplos conjuntos de resultados ou esquema. Um SqlDataReader.
ExecuteXmlReader O comando devolve XML na forma exigida por ExecuteXmlReader. Um XmlReader.

Cada método tem um equivalente assíncrono baseado em tarefas. Passe a CancellationToken quando o chamador precisa de parar de esperar. Para o 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 os valores através dos 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 devolve

Utilize ExecuteScalar quando precisar de um único 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 base de dados NULL.

Utilize ExecuteNonQuery para instruções que alteram dados. Este exemplo reverte a 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 as contagens de linhas afetadas, podendo ExecuteNonQuery assim devolver -1 mesmo quando uma instrução altera os dados.

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

Utilize uma transação local

Quando uma ligação tem uma SqlTransaction local ativa, atribui essa transação a todos os comandos executados na ligação. Este exemplo reverte a 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();

Clique Commit em vez de Rollback apenas quando a aplicação aceita 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ções e rollback, veja Transações Locais.

Defina um tempo limite de comando

CommandTimeout limita os tempos de espera da execução de comandos e da leitura cumulativa da rede, em segundos. Um comando utiliza a configuração da Command Timeout ligaçã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 timeout 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 os tempos de espera do pool. CommandTimeout começa após o início da execução do comando. Para mais informações, consulte Opções de ligação.

Utilize um timeout finito e um token de cancelamento em conjunto. O timeout define um limite para a operação da base de dados. Cancelar o token pode devolver o controlo 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 depois altere o seu valor após cada execução terminar:

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

Ligue Prepare apenas quando as medições o justifiquem. 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 resolução de problemas

Utilize SqlException à volta da operação da base de dados e inspecione a sua coleção Errors quando o servidor devolver mais do que um erro. Para erros de comando, avisos e mensagens informativas, veja Gerir erros e mensagens.

Utilize contadores de diagnóstico, rastreio de fontes de eventos e o guia de resolução de problemas do SqlClient quando um erro requer mais contexto. Aplique lógica de retentativa configurável apenas quando repetir o comando for seguro.