Wykonywanie polecenia

Używa się SqlCommand do wykonywania instrukcji języka Transact-SQL (T-SQL) lub procedur składowanych. Komenda wymaga otwartego SqlConnection. Zwolnij polecenie po użyciu i zwolnij obiekt readera przed ponownym użyciem połączenia.

Przykłady w tym artykule zakładają, że otwarte połączenie SqlConnection o nazwie connection wskazuje na przykładową bazę danych AdventureWorks.

Przykłady w języku C# wymagają using Microsoft.Data.SqlClient;. Przykłady używające SqlDbType, CommandType lub CommandBehavior również wymagają using System.Data;.

Aby dowiedzieć się o nawiązywaniu połączenia, uwierzytelnianiu i szyfrowaniu, zobacz Parametry połączenia oraz Szyfrowanie i sprawdzanie poprawności certyfikatu.

Wybierz metodę wykonania

Wybierz metodę odpowiadającą oczekiwanemu wynikowi:

Metoda Użyj, gdy Result
ExecuteNonQuery Polecenie zmienia dane lub schemat. Liczba zmodyfikowanych wierszy. -1 może wystąpić, gdy nie ma dostępnej liczby, w tym gdy SET NOCOUNT ON tłumi liczby.
ExecuteScalar Potrzebujesz jednej wartości. Pierwsza kolumna pierwszego wiersza lub null gdy wynik nie zawiera wierszy. Baza NULL danych jest zwracana jako Value.
ExecuteReader Wiersze, wiele zestawów wyników lub schemat. Element SqlDataReader.
ExecuteXmlReader Polecenie zwraca XML w postaci wymaganej przez ExecuteXmlReader. Jeden XmlReader.

Każda metoda ma asynchroniczny odpowiednik oparty na zadaniach. Przekaż CancellationToken, gdy dzwoniący musi przestać czekać. Informacje o zachowaniu anulowania i limicie czasu można znaleźć w sekcji Programowanie asynchroniczne.

Wykonaj polecenie tekstowe

Wartość domyślna to CommandType.Text. Umieść instrukcję w CommandText i przepuszczaj wartości przez parametry:

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

Informacje o typach parametrów, rozmiarach, wartościach null i kierunkach znajdują się w Konfiguruj parametry. Nie łącz niezaufanych wartości w CommandText.

Wykonaj procedurę składowaną

Ustaw CommandType.StoredProcedure i użyj nazwy procedury kwalifikowanej przez schemat:

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 obsługuje CommandType.Text i CommandType.StoredProcedure. Ustawienie CommandType.TableDirect powoduje ArgumentOutOfRangeException.

Sprawdź, co każda metoda zwraca

Używaj ExecuteScalar , gdy potrzebujesz jednej wartości:

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 zwraca null, gdy wynik nie zawiera wierszy, a ExecuteScalar zwraca DBNull.Value, gdy pierwsza wartość jest bazą danych NULL.

Użyj ExecuteNonQuery w instrukcjach zmieniających dane. Ten przykład cofa tę zmianę, więc AdventureWorks pozostaje bez zmian:

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 tłumi liczbę wierszy dotkniętych, więc ExecuteNonQuery może zwracać nawet -1 wtedy, gdy instrukcja zmienia dane.

Użyj ExecuteXmlReader, aby uzyskać efekt FOR XML:

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

Użyj transakcji lokalnej

Gdy połączenie ma aktywną lokalną transakcję SqlTransaction, przypisz tę transakcję do każdego polecenia wykonywanego na tym połączeniu. Ten przykład cofa tę zmianę, więc AdventureWorks pozostaje bez zmian:

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

Wywołaj Commit zamiast Rollback tylko wtedy, gdy aplikacja zaakceptuje całą transakcję. Polecenie, które nie odwołuje się do aktywnej transakcji, powoduje zgłoszenie wyjątku InvalidOperationException. Wykonanie również nie udaje się, jeśli transakcja należy do innego połączenia. Dla granic transakcji i wycofywania, zobacz Transakcje lokalne.

Ustaw limit czasu polecenia

CommandTimeout ogranicza wykonywanie poleceń i skumulowane oczekiwania odczytu sieciowego w sekundach. Polecenie korzysta z ustawienia połączenia Command Timeout, gdy jest skonfigurowane. W przeciwnym razie domyślnie to 30 sekund. Wartość 0 nie ma limitu czasowego. Wartości ujemne powodują zgłoszenie wyjątku ArgumentException.

Gdy upłynie limit czasu, polecenie rzuca SqlException z ustawionym Number 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 Ogranicza oczekiwania na nawiązanie połączenia i oczekiwania na zasoby puli. CommandTimeout rozpoczyna się po rozpoczęciu wykonywania polecenia. Więcej informacji można znaleźć w sekcji Opcje połączenia.

Używaj ograniczonego limitu czasu i tokena anulowania razem. Limit czasu operacji wyznacza limit dla operacji bazy danych. Anulowanie tokena może przywrócić kontrolę wywołującemu, zanim upłynie limit czasu. Nie zakładaj, że anulowanie i timeout dają ten sam wyjątek.

Ponowne wykorzystanie i przygotowanie poleceń

Dodaj parametr raz, a następnie zmieniaj jego wartość po zakończeniu każdego wykonania:

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

Zadzwoń Prepare tylko wtedy, gdy pomiary to uzasadniają. Prepare Nie przygotowuje procedur składowanych ani poleceń tekstowych bez parametrów. Dla tekstu parametryzowanego ustaw pełne metadane, takie jak Size, Scale i Prepare, dla każdego parametru przed wywołaniem Precision.

Polecenia rozwiązywania problemów

Przechwyć SqlException wokół operacji bazy danych i sprawdź jej kolekcję Errors, gdy serwer zwraca więcej niż jeden błąd. Informacje o błędach poleceń, ostrzeżeniach i komunikatach informacyjnych znajdziesz w Obsługa błędów i komunikatów.

Używaj liczników diagnostycznych, śledzenia źródła zdarzeń oraz przewodnika rozwiązywania problemów SqlClient, gdy błąd wymaga więcej kontekstu. Stosuj konfigurowalną logikę ponawiania tylko wtedy, gdy powtarzanie polecenia jest bezpieczne.