Spuštění příkazu

Pomocí SqlCommand můžete provádět příkazy Transact-SQL (T-SQL) nebo uložené procedury. Příkaz potřebuje otevřený SqlConnection. Po použití příkaz zlikvidujte a před opětovným použitím připojení zlikvidujte objekt reader.

Příklady v tomto článku předpokládají, že otevřené připojení SqlConnection s názvem connection odkazuje na ukázkovou databázi AdventureWorks.

Příklady v C# vyžadují using Microsoft.Data.SqlClient;. Příklady, které používají SqlDbType, CommandType, nebo CommandBehavior také vyžadují using System.Data;.

Pro nastavení spojení, autentizaci a šifrování viz Připojovací řetězce a Šifrování a ověřování certifikátů.

Vyberte způsob provádění

Vyberte metodu, která odpovídá očekávanému výsledku:

Metoda Použít, když Výsledek
ExecuteNonQuery Příkaz mění data nebo schéma. Počet ovlivněných řádků -1 může nastat, když není k dispozici žádný počet, včetně případů, kdy SET NOCOUNT ON potlačuje počty.
ExecuteScalar Potřebujete jednu hodnotu. První sloupec prvního řádku, nebo null když výsledek nemá žádné řádky. Databáze NULL se vrací jako Value.
ExecuteReader Potřebujete řádky, více sad výsledků nebo schéma. Nějaké SqlDataReader.
ExecuteXmlReader Příkaz vrací XML ve tvaru požadovaném prvkem ExecuteXmlReader. Nějaký XmlReader.

Každá metoda má asynchronní protějšek založený na úkolech. Předejte CancellationToken, když volající potřebuje přestat čekat. Pro chování při rušení a časovém limitu viz Asynchronní programování.

Spustit textový příkaz

CommandType.Text je výchozí hodnota. Vložte instrukci do CommandText a předávejte hodnoty prostřednictvím parametrů:

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

Pro typy parametrů, velikosti, hodnoty NULL a směry viz Konfigurace parametrů. Nevkládejte nedůvěryhodné hodnoty do CommandText.

Provedení uložené procedury

Nastavte CommandType.StoredProcedure a použijte název procedury kvalifikovaný schématem:

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 podporuje CommandType.Text a CommandType.StoredProcedure. Nastavení CommandType.TableDirect vyvolá ArgumentOutOfRangeException.

Zkontrolujte, co každá metoda vrací

Použijte ExecuteScalar , když potřebujete jednu hodnotu:

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 vrací null, když výsledek nemá žádné řádky a DBNull.Value, když je první hodnota databázová NULL.

Použijte ExecuteNonQuery pro příkazy měnící data. Tento příklad vrací zpět svou změnu, takže AdventureWorks zůstává beze změny:

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 potlačuje počty ovlivněných řádků, takže ExecuteNonQuery může vrátit i -1 tehdy, když příkaz změní data.

Použijte ExecuteXmlReader pro FOR XML výsledek:

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

Použijte lokální transakci

Když má spojení aktivní lokální SqlTransaction, je této transakci přiřazen každý příkaz provedený v tomto spojení. Tento příklad vrátí svou změnu zpět, takže AdventureWorks zůstává beze změny:

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

Volejte Commit místo Rollback pouze tehdy, když aplikace přijme celou transakci. Příkaz, který neodkazuje na aktivní transakci, hodí InvalidOperationException. Provádění také selže, pokud transakce patří jinému připojení. Pro hranice transakcí a rollback viz Lokální transakce.

Nastavte časový limit příkazu

CommandTimeout omezuje provádění příkazů a kumulativní čekání na čtení ze sítě v sekundách. Příkaz používá nastavení připojení Command Timeout, pokud je nakonfigurováno. Jinak je výchozí doba 30 sekund. Hodnota 0 nemá žádné časové omezení. Záporné hodnoty vyvolají ArgumentException.

Když vyprší časový limit, příkaz hodí SqlException s Number nastaveným na -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 Omezuje navazování spojení a čekání ve frontě poolu. CommandTimeout začíná po spuštění příkazu. Pro více informací viz Možnosti připojení.

Používejte konečný časový limit a token zrušení společně. Časový limit stanovuje limit pro databázovou operaci. Zrušení tokenu může vrátit kontrolu volajícímu před vypršením timeoutu. Nepředpokládejte, že zrušení a časový limit vyvolají stejnou výjimku.

Znovu použít a připravit příkazy

Přidejte parametr jednou a poté po dokončení každého spuštění změňte jeho hodnotu:

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

Použijte Prepare jen tehdy, když to naměřené hodnoty ospravedlňují. Prepare Nepřipravuje uložené procedury ani textové příkazy bez parametrů. Pro parametrizovaný text nastavte kompletní metadata, jako jsou Size, Precision a Scale, na každý parametr předtím, než zavoláte Prepare.

Příkazy k řešení problémů

Zachyťte SqlException při databázové operaci a zkontrolujte její Errors kolekci chyb, když server vrátí více než jednu chybu. Pro chyby příkazů, varování a informační zprávy viz Zvládání chyb a zpráv.

Používejte diagnostické čítače, trasování zdrojů událostí a průvodce SqlClient pro řešení problémů, když chyba vyžaduje více kontextu. Aplikujte konfigurovatelnou logiku opakovaného pokusu pouze tehdy, když je bezpečné opakovat příkaz.