Obsługa przesyłania strumieniowego SqlClient

Używaj metod strumieniowania Microsoft.Data.SqlClient do przetwarzania dużych wartości bez wcześniejszego ładowania całej wartości do pamięci aplikacji. Przesyłanie strumieniowe jest przydatne w przypadku plików binarnych, dużych tekstów, XML, JSON oraz transferu między serwerami.

Streaming nie sprawia, że wartość ma możliwość ustawiania pozycji ani nie czyni jednoczesnych operacji na czytniku bezpiecznymi. Połączenie, polecenie, czytnik, strumień źródłowy oraz obiekt docelowy muszą pozostać aktywne do zakończenia transferu.

Przykłady w tym artykule zakładają, że otwarte połączenie SqlConnection o nazwie connection jest skierowane do przykładowej bazy danych AdventureWorks.

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

Strumieniowanie danych z bazy SQL

Wykonaj polecenie za pomocą CommandBehavior.SequentialAccess i odczytuj kolumny według kolejności porządkowej. W dostępie sekwencyjnym, po przejściu dalej niż dana kolumna, nie czytaj wcześniejszej kolumny w tym samym wierszu.

Stosuj następujące SqlDataReader metody:

Metoda Result Typowe dane SQL
GetStream Stream dane binarne, varbinary, image oraz dane typu zdefiniowanego przez użytkownika (UDT)
GetTextReader TextReader Charakterystyka i dane JSON
GetXmlReader XmlReader xml

Wywołaj IsDBNull przed zażądaniem strumienia dla kolumny nullable. Metoda pobierająca strumieniowo używana z niekompatybilnym typem SQL zgłasza wyjątek. Odczytaj do końca lub zamknij bieżący strumień przed przeczytaniem kolejnej kolumny, przesunięciem wiersza do przodu lub przejściem do następnego wyniku.

GetStream nie jest obsługiwany dla zaszyfrowanych kolumn. GetTextReader nie obsługuje sekwencyjnego dostępu do zaszyfrowanej kolumny.

CommandTimeout ma zastosowanie, gdy Microsoft.Data.SqlClient czeka na pakiety sieciowe podczas wykonywania i odczytu. Czas, który twoja aplikacja poświęca na przetwarzanie już otrzymanych danych, nie jest liczony. O anulowaniu i limitach czasu zobacz Programowanie asynchroniczne.

Przesyłaj strumieniowo dane binarne

AdventureWorks przechowuje zawartość plików w kolumnie varbinary(max)Production.Document.Document dopuszczającej wartość NULL. Ten przykład kopiuje jedną wartość do pliku z ograniczonym buforem:

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

W przypadku tekstu użyj GetTextReader i skopiuj do TextWriter schowka. W przypadku XML używaj GetXmlReader i przetwarzaj węzły XML bez konwersji pełnej wartości na ciąg łańcuchowy.

Przesyłaj dane do bazy danych SQL

Ustaw Value na obiekt streamingowy i wybierz kompatybilny typ SQL:

Typ wartości Zgodne typy parametrów
Stream Binary, VarBinary, oraz starszy typ Image
TextReader Char, NChar, VarChar, NVarChar, starsze typy Text i NText, Xml, oraz Json
XmlReader Char, NChar, VarChar, NVarChar, starsze typy Text i NText, Xml oraz Json

Ustaw Size jawnie. Size Ogranicza przelewaną kwotę. Użyj -1 dla parametru (max), gdy chcesz wysłać kompletne źródło.

W przypadku przesyłania z ograniczoną pamięcią używaj VarBinary, VarChar lub NVarChar z Size = -1, lub używaj Xml lub Json. Microsoft.Data.SqlClient buforuje źródło do tablicy bajtów lub ciągu przed wykonaniem dla typów innych niż (max) znakowe i binarne oraz starszych typów Image, Text i NText.

Przesyłaj strumieniowo wartość parametru

Ten przykład przesyła plik przez parametr varbinary(max) i zwraca liczbę otrzymanych bajtów. Nie przechowuje wartości:

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

Zachowaj obiekt wejściowy otwarty do zakończenia wykonania polecenia. Przekaż token anulowania do asynchronicznej metody wykonania lub wywołaj Cancel, aby zażądać anulowania operacji strumieniowania. Anulowanie jest realizowane w miarę możliwości. Operacja może zakończyć się, zanim anulowanie zostanie zastosowane.

Wyjątek ze strumienia źródłowego lub czytnika powoduje błąd wykonania polecenia. Nie próbuj ponownie z tego samego obiektu, chyba że uda Ci się ustalić jego pozycję i operacja bazy danych jest bezpieczna do powtórzenia.

Przesyłaj dane z jednej bazy SQL do drugiej

Ten przykład przekazuje strumień dokumentów AdventureWorks bezpośrednio do parametru w innym otwartym elemencie SqlConnection o nazwie destinationConnection. Przykładowe polecenie podaje długość odebranych danych. Zastąp go parametryzowanym poleceniem wstawienia lub aktualizacji w docelowej bazie danych:

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

Polecenia źródłowe i docelowe nie korzystają z tej samej transakcji. Jeśli twoja aplikacja wymaga skoordynowanych operacji atomowych na serwerach, zaprojektuj i przetestuj tę granicę transakcyjną osobno.