Eseguire un comando

Usare SqlCommand per eseguire istruzioni Transact-SQL (T-SQL) o stored procedure. Un comando ha bisogno di un file aperto SqlConnection. Chiudi il comando dopo l'uso e chiudi qualsiasi reader prima di riutilizzare la connessione.

Gli esempi in questo articolo presuppongono che un oggetto aperto connection denominato SqlConnection punti al database di esempio AdventureWorks.

Gli esempi di C# richiedono using Microsoft.Data.SqlClient;. Esempi che usano SqlDbType, CommandType o CommandBehavior richiedono anch'essi using System.Data;.

Per la configurazione, l'autenticazione e la crittografia della connessione, vedi Stringhe di connessione e Crittografia e validazione dei certificati.

Scegli un metodo di esecuzione

Scegli il metodo che corrisponde al risultato atteso:

Metodo Usa quando Risultato
ExecuteNonQuery Il comando modifica i dati o lo schema. Il numero di righe interessate. -1 può verificarsi quando non è disponibile conteggio, anche quando SET NOCOUNT ON sopprime i conteggi.
ExecuteScalar Ti serve un solo valore. La prima colonna della prima riga, o null quando il risultato non ha righe. Un database NULL viene restituito come Value.
ExecuteReader Servono righe, più set di risultati o schema. Un oggetto SqlDataReader.
ExecuteXmlReader Il comando restituisce XML nella forma richiesta da ExecuteXmlReader. Un oggetto XmlReader.

Ogni metodo ha un corrispettivo asincrono basato su compiti. Fornisci CancellationToken quando il chiamante deve smettere di aspettare. Per il comportamento di cancellazione e timeout, vedi Programmazione asincrona.

Esegui un comando di testo

CommandType.Text è l'impostazione predefinita. Inserisci l'istruzione in CommandText e passa i valori tramite parametri:

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

Per tipi di parametri, dimensioni, valori nulli e direzioni, vedi Configura parametri. Non concatenare valori non affidabili in CommandText.

Eseguire una procedura memorizzata

Imposta CommandType.StoredProcedure e usa il nome della procedura qualificato con lo schema:

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 supporta CommandType.Text e CommandType.StoredProcedure. L'impostazione di CommandType.TableDirect genera ArgumentOutOfRangeException.

Controlla cosa restituisce ogni metodo

Usalo ExecuteScalar quando hai bisogno di un valore:

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 ritorna null quando il risultato non ha righe e DBNull.Value quando il primo valore è un valore NULL del database NULL.

Da usare ExecuteNonQuery per istruzioni che modificano i dati. Questo esempio annulla la sua modifica in modo che AdventureWorks rimanga invariato:

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 sopprime i conteggi delle righe interessate, quindi ExecuteNonQuery può restituire -1 anche quando un'istruzione modifica i dati.

Usa ExecuteXmlReader per un FOR XML risultato:

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

Usa una transazione locale

Quando una connessione ha una transazione locale SqlTransaction attiva, assegna quella transazione a ogni comando eseguito sulla connessione. Questo esempio annulla la sua modifica in modo che AdventureWorks rimanga invariato:

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

Chiama Commit invece di Rollback solo quando l'applicazione accetta la transazione completa. Un comando che non fa riferimento alla transazione attiva genera InvalidOperationException. L'esecuzione fallisce anche se la transazione appartiene a un'altra connessione. Per i confini delle transazioni e il rollback, vedi Transazioni locali.

Imposta un timeout di comando

CommandTimeout limita l'esecuzione dei comandi e l'attesa cumulativa della lettura di rete in secondi. Un comando utilizza l'impostazione della Command Timeout connessione quando una è configurata. Altrimenti, il valore predefinito è 30 secondi. Un valore di 0 non ha limite temporale. I valori negativi generano un'eccezione ArgumentException.

Quando il timeout scade, il comando genera SqlException con Number impostato a -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 l'istituzione delle connessioni e le attese del pool. CommandTimeout inizia dopo l'inizio dell'esecuzione del comando. Per maggiori informazioni, consulta Opzioni di connessione.

Usa insieme un timeout finito e un token di cancellazione. Il timeout stabilisce un limite all'operazione del database. Cancellare il token può restituire il controllo al chiamante prima che scada il timeout. Non dare per scontato che cancellazione e timeout producano la stessa eccezione.

Riutilizzare e preparare i comandi

Aggiungi un parametro una volta, e poi cambia il suo valore dopo che ogni esecuzione è terminata:

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

Chiama Prepare solo quando le misurazioni lo giustificano. Prepare Non prepara stored procedure o comandi di testo senza parametri. Per il testo parametrizzato, imposta metadati completi come Size, Precision, e Scale su ogni parametro prima di chiamare Prepare.

Comandi di risoluzione dei problemi

Gestisci con SqlException l'operazione del database e ispeziona la raccolta Errors associata quando il server restituisce più di un errore. Per errori di comando, avvisi e messaggi informativi, vedi Gestire errori e messaggi.

Usa i contatori diagnostici, il tracciamento della sorgente eventi e la guida alla risoluzione dei problemi SqlClient quando un errore richiede più contesto. Applicare la logica di ritentazione configurabile solo quando ripetere il comando è sicuro.