SqlClient-Streamingunterstützung

Verwenden Sie Microsoft. Data.SqlClient-Streaming-Methoden, um große Werte zu verarbeiten, ohne vorher den vollständigen Wert in den Anwendungsspeicher zu laden. Streaming ist nützlich für Binärdateien, große Texte, XML, JSON und Server-zu-Server-Übertragungen.

Streaming macht den Wert nicht wahlfrei zugreifbar und macht gleichzeitige Operationen auf einem Reader nicht sicher. Die Verbindung, der Befehl, der Leser, der Quellstrom und das Zielobjekt müssen bestehen bleiben, bis die Übertragung abgeschlossen ist.

Die Beispiele in diesem Artikel gehen davon aus, dass eine offene SqlConnection mit dem Namen connection die AdventureWorks-Beispieldatenbank anspricht.

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

Daten aus einer SQL-Datenbank streamen

Führe den Befehl mit CommandBehavior.SequentialAccess und lies die Spalten in ordinaler Reihenfolge. Bei sequentiellem Zugriff solltest du, nachdem du an einer Spalte vorbeigegangen bist, keine frühere Spalte in derselben Reihe lesen.

Verwenden Sie diese SqlDataReader Methoden:

Methode Ergebnis Typische SQL-Daten
GetStream Stream binary, varbinary, image und Daten vom benutzerdefinierten Typ (UDT)
GetTextReader TextReader Zeichen- und JSON-Daten
GetXmlReader XmlReader xml

Rufen Sie IsDBNull an, bevor Sie einen Stream für eine nullable Spalte anfordern. Ein Streaming-Getter, der mit einem inkompatiblen SQL-Typ verwendet wird, löst eine Ausnahme aus. Verbrauchen oder verwerfen Sie den aktuellen Stream, bevor Sie eine andere Spalte lesen, zur nächsten Zeile wechseln oder zum nächsten Ergebnis übergehen.

GetStream wird für verschlüsselte Spalten nicht unterstützt. GetTextReader unterstützt keinen sequentiellen Zugriff auf eine verschlüsselte Spalte.

CommandTimeout gilt, während Microsoft.Data.SqlClient während der Ausführung und des Lesens auf Netzwerkpakete wartet. Die Zeit, die Ihre Anwendung mit der Verarbeitung bereits erhaltener Daten verbringt, wird nicht angerechnet. Für Abbruch und Zeitüberschreitungen siehe Asynchrone Programmierung.

Binärdaten streamen

AdventureWorks speichert Dateiinhalte in der NULL-zulässigen varbinary(max)Production.Document.Document Spalte. Dieses Beispiel kopiert einen Wert in eine Datei mit begrenztem Puffer:

using var command = new SqlCommand("""
    SELECT TOP (1) Document
    FROM Production.Document
    WHERE Document IS NOT NULL
    ORDER BY ModifiedDate DESC;
    """, connection);

using SqlDataReader reader = command.ExecuteReader(
    CommandBehavior.SequentialAccess);
if (reader.Read())
{
    using Stream source = reader.GetStream(0);
    using FileStream destination = File.Create("document.bin");
    source.CopyTo(destination);
}

Verwenden Sie für Text GetTextReader und kopieren Sie es in ein TextWriter. Verwenden Sie für XML GetXmlReader und bearbeiten Sie XML-Knoten, ohne den vollständigen Wert in eine Zeichenkette umzuwandeln.

Daten in eine SQL-Datenbank streamen

Setze Value auf ein Streaming-Objekt und wähle einen kompatiblen SQL-Typ:

Werttyp Kompatible Parametertypen
Stream Binary, VarBinary und der Legacy-Image-Typ
TextReader Char, Text, NText, Xml, die Legacy-Typen NVarChar und VarChar, NChar und Json
XmlReader Char, Text, NText, Xml, die Legacytypen NVarChar und VarChar, NChar und Json

Legen Sie Size explizit fest. Size begrenzt den überwiesenen Betrag. Verwenden Sie -1 für einen (max)-Parameter, wenn Sie die vollständige Quelle senden möchten.

Für den Transfer mit beschränktem Speicher verwenden Sie VarBinary, VarChar oder NVarChar mit Size = -1, oder verwenden Sie Xml oder Json. Microsoft.Data.SqlClient puffert die Quelldaten vor der Ausführung in ein Byte-Array oder einen String für Nicht-(max)Zeichen- und Binärtypen sowie für die Legacy-Image-, Text- und NText-Typen.

Parameterwert streamen

Dieses Beispiel sendet eine Datei über einen varbinary(max)-Parameter und gibt die Anzahl der empfangenen Bytes zurück. Der Wert wird nicht gespeichert:

using FileStream input = File.OpenRead("document.bin");
using var command = new SqlCommand(
    "SELECT DATALENGTH(@content);",
    connection);
command.Parameters.Add(
    new SqlParameter("@content", SqlDbType.VarBinary, -1)
    {
        Value = input
    });

long length = (long)command.ExecuteScalar()!;
Console.WriteLine($"{length} bytes");

Lassen Sie das Eingabeobjekt offen, bis die Befehlsausführung abgeschlossen ist. Geben Sie ein Abbruchtoken an eine asynchrone Ausführungsmethode weiter, oder rufen Sie Cancel auf, um den Abbruch der Streaming-Operation anzufordern. Stornierung erfolgt nach bestem Bemühen. Die Operation könnte abgeschlossen werden, bevor der Abbruch wirksam wird.

Eine Ausnahme aus dem Quellstream oder ein Leserfehler führt zu einem Fehler bei der Befehlsausführung. Versuche es nicht erneut mit demselben Objekt, es sei denn, du kannst seine Position bestimmen und die Datenbankoperation kann sicher wiederholt werden.

Daten von einer SQL-Datenbank in eine andere streamen

Dieses Beispiel übergibt einen AdventureWorks-Dokumentstrom direkt an einen Parameter in einem anderen geöffneten SqlConnection namens destinationConnection. Der Musterbefehl gibt die empfangene Länge an. Ersetzen Sie ihn durch den parametrisierten Ein- oder Aktualisierungsbefehl der Zieldatenbank:

using var readSource = new SqlCommand("""
    SELECT TOP (1) Document
    FROM Production.Document
    WHERE Document IS NOT NULL
    ORDER BY ModifiedDate DESC;
    """, connection);

using SqlDataReader sourceReader = readSource.ExecuteReader(
    CommandBehavior.SequentialAccess);

if (sourceReader.Read())
{
    using Stream content = sourceReader.GetStream(0);
    using var write = new SqlCommand(
        "SELECT DATALENGTH(@content);",
        destinationConnection);
    write.Parameters.Add(
        new SqlParameter("@content", SqlDbType.VarBinary, -1)
        {
            Value = content
        });

    Console.WriteLine(write.ExecuteScalar());
}

Die Quell- und Zielbefehle teilen sich keine Transaktion. Wenn Ihre Anwendung koordinierte atomare Arbeit über Server hinweg erfordert, entwerfen und testen Sie diese Transaktionsgrenze separat.