Ausführen von Befehlen

Verwenden Sie SqlCommand, um Transact-SQL (T-SQL)-Anweisungen oder gespeicherte Prozeduren auszuführen. Ein Befehl braucht ein offenes SqlConnection. Entsorge den Command nach der Nutzung und entsorge alle Reader, bevor du die Verbindung wiederverwendest.

Die Beispiele in diesem Artikel gehen davon aus, dass ein geöffnetes SqlConnection namens connection die AdventureWorks-Beispieldatenbank anspricht.

Die C#-Beispiele erfordern using Microsoft.Data.SqlClient;. Beispiele, die SqlDbType, CommandType oder CommandBehavior verwenden, erfordern ebenfalls using System.Data;.

Für Verbindungsaufbau, Authentifizierung und Verschlüsselung siehe Verbindungszeichenfolgen und Verschlüsselung und Zertifikatsvalidierung.

Wählen Sie eine Ausführungsmethode

Wählen Sie die Methode, die dem erwarteten Ergebnis entspricht:

Methode Verwenden Sie, wenn Ergebnis
ExecuteNonQuery Der Befehl ändert Daten oder Schema. Die Anzahl der betroffenen Zeilen. -1 kann auftreten, wenn keine Anzahl verfügbar ist, auch wenn SET NOCOUNT ON die Anzahl unterdrückt.
ExecuteScalar Du brauchst einen Wert. Die erste Spalte der ersten Zeile, oder null wenn das Ergebnis keine Zeilen hat. Eine Datenbank NULL wird als Valuezurückgegeben.
ExecuteReader Du brauchst Zeilen, mehrere Ergebnissets oder ein Schema. Ein SqlDataReader.
ExecuteXmlReader Der Befehl gibt XML in der von ExecuteXmlReader benötigten Form zurück. Ein XmlReader-Element.

Jede Methode hat ein aufgabenbasiertes asynchrones Gegenstück. Übergeben Sie CancellationToken, wenn der Anrufer aufhören muss zu warten. Für Abbruch- und Timeoutverhalten siehe Asynchrone Programmierung.

Führe einen Textbefehl aus

CommandType.Text ist der Standardwert. Platzieren Sie die Anweisung in CommandText und leiten Sie Werte durch Parameter durch:

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 Parametertypen, Größen, Nullwerte und Richtungen siehe Parameter konfigurieren. Hänge keine nicht vertrauenswürdigen Werte an CommandText an.

Ausführen einer gespeicherten Prozedur

Setzen CommandType.StoredProcedure und verwenden Sie den schema-qualifizierten Prozedurnamen:

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 unterstützt CommandType.Text und CommandType.StoredProcedure. Das Setzen von CommandType.TableDirect löst ArgumentOutOfRangeException aus.

Überprüfe, was jede Methode zurückgibt

Verwenden ExecuteScalar Sie, wenn Sie einen Wert benötigen:

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 gibt null zurück, wenn das Ergebnis keine Zeilen hat, und DBNull.Value, wenn der erste Wert ein Datenbank-NULL ist.

Verwenden Sie ExecuteNonQuery für datenverändernde Anweisungen. Dieses Beispiel macht seine Änderung rückgängig, sodass AdventureWorks unverändert bleibt:

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 unterdrückt betroffene Zeilenzählungen, sodass ExecuteNonQuery auch dann -1 zurückgeben kann, wenn eine Anweisung Daten ändert.

Verwenden Sie ExecuteXmlReader für ein FOR XML Ergebnis:

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

Verwenden Sie eine lokale Transaktion

Wenn eine Verbindung ein aktives lokales SqlTransaction hat, weisen Sie diese Transaktion jedem auf der Verbindung ausgeführten Befehl zu. Dieses Beispiel macht seine Änderung rückgängig, sodass AdventureWorks unverändert bleibt:

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

Rufen Sie nur Rollback statt Commit auf, wenn die Anwendung die vollständige Transaktion akzeptiert. Ein Befehl, der sich nicht auf die aktive Transaktion bezieht, löst InvalidOperationException aus. Die Ausführung schlägt ebenfalls fehl, wenn die Transaktion zu einer anderen Verbindung gehört. Für Transaktionsgrenzen und Rollback siehe Lokale Transaktionen.

Befehlszeitlimit festlegen

CommandTimeout legt Grenzwerte in Sekunden für die Befehlsausführung und kumulierte Wartezeiten bei Netzwerklesevorgängen fest. Ein Befehl verwendet die Command Timeout Einstellung der Verbindung, wenn eine konfiguriert ist. Andernfalls beträgt der Standardwert 30 Sekunden. Ein Wert von 0 hat keine Zeitbegrenzung. Negative Werte lösen ArgumentException aus.

Wenn das Timeout abläuft, wirft der Befehl SqlException, wobei Number auf -2 gesetzt ist:

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 Begrenzt die Verbindungseinrichtung und die Wartezeiten im Pool. CommandTimeout startet, nachdem die Befehlsausführung begonnen hat. Weitere Informationen finden Sie unter Verbindungsoptionen.

Verwenden Sie zusammen ein begrenztes Timeout und ein Abbruchtoken. Der Timeout setzt eine Begrenzung für den Datenbankvorgang. Das Abbrechen des Tokens kann die Steuerung an den Aufrufer zurückgeben, bevor der Timeout abläuft. Gehen Sie nicht davon aus, dass Abbruch und Timeout dieselbe Ausnahme auslösen.

Befehle wiederverwenden und vorbereiten

Füge einmal einen Parameter hinzu und ändere dann seinen Wert nach Abschluss jeder Ausführung:

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

Rufen Sie Prepare nur an, wenn Messungen es rechtfertigen. Prepare unterstützt keine vorbereiteten gespeicherten Prozeduren oder Textbefehle ohne Parameter. Für parametrisierten Text setze vollständige Metadaten wie Size, Scale und Prepare auf jeden Parameter, bevor du Precision aufrufst.

Befehle zur Problembehandlung

Fangen Sie SqlException um die Datenbankoperation ab und inspizieren Sie deren Errors Collection, wenn der Server mehr als einen Fehler zurückgibt. Für Befehlsfehler, Warnungen und Informationsnachrichten siehe Fehler und Meldungen behandeln.

Verwenden Sie Diagnosezähler, Ereignisquellenverfolgung und den SqlClient-Fehlerbehebungsleitfaden, wenn ein Fehler mehr Kontext erfordert. Wenden Sie konfigurierbare Wiederholungslogik nur an, wenn das Wiederholen des Befehls sicher ist.