Supporto dello streaming in SqlClient

Usa i metodi di streaming di Microsoft.Data.SqlClient per elaborare valori grandi senza prima caricare l'intero valore nella memoria dell'applicazione. Lo streaming è utile per file binari, testo di grandi dimensioni, XML, JSON e trasferimenti da server a server.

Lo streaming non rende il valore posizionabile né rende sicure le operazioni simultanee su un lettore. La connessione, il comando, il lettore, il flusso sorgente e l'oggetto destinazione devono rimanere attivi fino al termine del trasferimento.

Gli esempi in questo articolo presuppongono che un elemento connection aperto denominato SqlConnection sia destinato al database di esempio AdventureWorks.

Gli esempi di C# richiedono using Microsoft.Data.SqlClient;. Esempi che usano SqlDbType o CommandBehavior richiedono using System.Data;anche .

Trasmetti dati da un database SQL

Esegui il comando con CommandBehavior.SequentialAccess e leggi le colonne in ordine ordinale. Con l'accesso sequenziale, dopo aver superato una colonna, non leggere una colonna precedente nella stessa riga.

Usa questi SqlDataReader metodi:

Metodo Risultato Dati SQL tipici
GetStream Stream binary, varbinary, immagine e tipo definito dall'utente (UDT)
GetTextReader TextReader Dati di caratteri e JSON
GetXmlReader XmlReader xml

Chiama IsDBNull prima di richiedere uno stream per una colonna nullable. Un metodo getter di streaming usato con un tipo SQL incompatibile genera un'eccezione. Consuma o elimina il flusso corrente prima di leggere un'altra colonna, avanzare la riga o passare al risultato successivo.

GetStream non è supportato per colonne criptate. GetTextReader non supporta l'accesso sequenziale a una colonna crittografata.

CommandTimeoutsi applica mentre Microsoft.Data.SqlClient attende i pacchetti di rete durante l'esecuzione e la lettura. Il tempo che la tua applicazione impiega a elaborare i dati già ricevuti non viene conteggiato. Per cancellazioni e timeout, vedi Programmazione asincrona.

Flusso di dati binari

AdventureWorks memorizza il contenuto dei file nella colonna che ammette valori null varbinary(max)Production.Document.Document. Questo esempio copia un valore su un file con un buffer limitato:

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

Per il testo, usa GetTextReader e copia in un TextWriter. Per XML, usa GetXmlReader ed elabora i nodi XML senza convertire il valore completo in string.

Eseguire lo streaming dei dati in un database SQL

Imposta Value su un oggetto streaming e scegli un tipo SQL compatibile:

Tipo di valore Tipi di parametri compatibili
Stream Binary, VarBinary, e il tipo legacy Image
TextReader Char, NChar, VarChar, NVarChar, i tipi legacy Text e NText, Xml, e Json
XmlReader Char, NChar, VarChar, NVarChar, i tipi legacy Text e NText, Xml, e Json

Impostare Size in modo esplicito. Size limita l'importo trasferito. Usa -1 come parametro (max) quando vuoi inviare la sorgente completa.

Per il trasferimento a memoria limitata, si usa VarBinary, VarChar, o NVarChar con Size = -1, oppure usa Xml o Json. Microsoft.Data.SqlClient memorizza temporaneamente la sorgente in un array di byte o una stringa prima dell'esecuzione per i tipi non-(max) carattere e binari e per i tipi legacy Image, Text e NText.

Trasmetti un valore di parametro

Questo esempio invia un file attraverso un parametro varbinary(max) e restituisce il numero di byte ricevuti. Non memorizza il valore:

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

Tieni l'oggetto di input aperto fino al completamento dell'esecuzione del comando. Passa un token di cancellazione a un metodo di esecuzione asincrono o chiama Cancel per richiedere la cancellazione dell'operazione di streaming. La cancellazione è effettuata nei limiti del possibile. L'operazione potrebbe concludersi prima che la cancellazione entri in vigore.

Un'eccezione del flusso di origine o un errore del lettore causa un errore nell'esecuzione del comando. Non riprovare dallo stesso oggetto a meno che tu non riesca a stabilirne la posizione e l'operazione del database sia sicura da ripetere.

Trasmetti dati da un database SQL a un altro

Questo esempio passa un flusso di documenti AdventureWorks direttamente a un parametro di un altro SqlConnection aperto chiamato destinationConnection. Il comando di esempio riporta la lunghezza ricevuta. Sostituiscilo con il comando parametrizzato insert o update del database di destinazione:

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

I comandi sorgente e destinazione non condividono una transazione. Se la tua applicazione richiede un lavoro atomico coordinato tra server, progetta e testa separatamente quel confine di transazione.