Köra ett kommando

Använd SqlCommand för att köra Transact-SQL (T-SQL)-satser eller lagrade procedurer. Ett kommando behöver en öppen SqlConnection. Kassera kommandot efter användning och släng eventuell data reader innan du återanvänder anslutningen.

Exemplen i denna artikel förutsätter att en öppen SqlConnection med namnet connection är riktad mot AdventureWorks-exempeldatabasen.

C#-exemplen kräver using Microsoft.Data.SqlClient;. Exempel som använder SqlDbType, CommandBehavior eller using System.Data; kräver också CommandType.

För anslutningskonfiguration, autentisering och kryptering, se Anslutningssträngar och Kryptering och certifikatvalidering.

Välj en exekveringsmetod

Välj metoden som matchar det förväntade resultatet:

Method Använd när Result
ExecuteNonQuery Kommandot ändrar data eller schema. Antalet rader som påverkas. -1 kan inträffa när inget antal är tillgängligt, inklusive när SET NOCOUNT ON undertrycker antal.
ExecuteScalar Du behöver ett värde. Den första kolumnen i första raden, eller null när resultatet inte har några rader. En databas NULL returneras i form av Value.
ExecuteReader Du behöver rader, flera resultatuppsättningar eller schema. En SqlDataReader.
ExecuteXmlReader Kommandot returnerar XML i den form som krävs av ExecuteXmlReader. En XmlReader.

Varje metod har en uppgiftsbaserad asynkron motsvarighet. Skicka med a CancellationToken när anroparen behöver sluta vänta. För avbokning och timeout-beteende, se Asynkron programmering.

Utför ett textkommando

CommandType.Text används som standard. Placera satsen i CommandText och skicka värden genom parametrar:

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

För parametertyper, storlekar, nollvärden och riktningar, se Konfigurera parametrar. Sammanfoga inte opålitliga värden i CommandText.

Utföra en lagrad procedur

Sätt CommandType.StoredProcedure och använd namnet på schema-kvalificerad procedur:

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 stöder CommandType.Text och CommandType.StoredProcedure. Att ställa in CommandType.TableDirect utlöser ArgumentOutOfRangeException.

Kontrollera vad varje metod ger

Använd ExecuteScalar när du behöver ett värde:

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 returnerar null när resultatet inte har några rader och DBNull.Value när det första värdet är en databas NULL.

Använd ExecuteNonQuery för dataändrande instruktioner. Detta exempel rullar tillbaka sin ändring så att AdventureWorks förblir oförändrat:

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 undertrycker berörda radräkningar, så ExecuteNonQuery kan returnera -1 även när en sats ändrar data.

Använd ExecuteXmlReader för ett FOR XML resultat:

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

Använd en lokal transaktion

När en anslutning har en aktiv lokal SqlTransaction tilldelar du den transaktionen till varje kommando som körs på anslutningen. Detta exempel rullar tillbaka sin ändring så att AdventureWorks förblir oförändrat:

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

Ring Commit istället för Rollback bara när applikationen accepterar hela transaktionen. Ett kommando som inte refererar till den aktiva transaktionen kastar InvalidOperationException. Exekveringen misslyckas också om transaktionen tillhör en annan anslutning. För transaktionsgränser och rollback, se Lokala transaktioner.

Sätt en kommandotimeout

CommandTimeout begränsar kommandoexekvering och kumulativa nätverksläsväntetider i sekunder. Ett kommando använder anslutningens Command Timeout inställning när ett sådant är konfigurerat. Annars är standardläget 30 sekunder. Ett värde av 0 har ingen tidsgräns. Negativa värden ger upphov till ArgumentException.

När timeouten går ut kastar kommandot SqlException med Number inställt på -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 Begränsar anslutningsetablering och poolväntetider. CommandTimeout börjar efter att kommandoexekveringen har börjat. För mer information, se Anslutningsalternativ.

Använd en begränsad timeout och en avbrottstoken tillsammans. Tidsgränsen sätter en gräns för databasoperationen. Att annullera tokenen kan återlämna kontrollen till anroparen innan timeouten går ut. Anta inte att avbokning och timeout ger samma undantag.

Återanvänd och förbered kommandon

Lägg till en parameter en gång och ändra dess värde efter att varje exekvering har slutförts:

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

Ring Prepare bara när mätningarna motiverar det. Prepare förbereder inte lagrade procedurer eller textkommandon utan parametrar. För parameteriserad text, ställ in komplett metadata såsom Size, Precision, och Scale på varje parameter innan du anropar Prepare.

Felsökningskommandon

Fånga SqlException kring databasåtgärden och inspektera dess Errors samling när servern returnerar mer än ett fel. För kommandofel, varningar och informationsmeddelanden, se Hantera fel och meddelanden.

Använd diagnostiska räknare, händelsekällspårning och SqlClients felsökningsguide när ett fel kräver mer kontext. Använd konfigurerbar återförsökslogik endast när det är säkert att upprepa kommandot.