Suporte ao streaming do SqlClient

Utilize métodos de streaming do Microsoft.Data.SqlClient para processar valores de grande dimensão sem antes carregar o valor completo na memória da aplicação. O streaming é útil para ficheiros binários, texto grande, XML, JSON e transferência de servidor para servidor.

O streaming não torna o valor procurável nem as operações simultâneas num leitor seguras. A ligação, o comando, o leitor, o fluxo de origem e o objeto de destino devem permanecer ativos até ao término da transferência.

Os exemplos neste artigo assumem que uma ligação connection aberta denominada SqlConnection tem como alvo a base de dados de exemplo do AdventureWorks.

Os exemplos de C# requerem using Microsoft.Data.SqlClient;. Exemplos que usam SqlDbType ou CommandBehavior também exigem using System.Data;.

Transmitir dados a partir de uma base de dados SQL

Execute o comando com CommandBehavior.SequentialAccess e leia as colunas por ordem ordinal. Com acesso sequencial, depois de avançar para além de uma coluna, não leia uma coluna anterior na mesma linha.

Utilize estes SqlDataReader métodos:

Method Result Dados SQL típicos
GetStream Stream binary, varbinary, image e dados de tipo definido pelo utilizador (UDT)
GetTextReader TextReader Dados de caracteres e JSON
GetXmlReader XmlReader xml

Ligue IsDBNull antes de pedir um stream para uma coluna que aceita nulos. Um getter de streaming usado com um tipo SQL incompatível lança uma exceção. Consumir ou eliminar o stream atual antes de ler outra coluna, avançar a linha ou passar para o resultado seguinte.

GetStream não é suportado para colunas encriptadas. GetTextReader não suporta acesso sequencial a uma coluna encriptada.

CommandTimeoutaplica-se enquanto a Microsoft. Data.SqlClient espera pelos pacotes de rede durante a execução e leitura. O tempo que a sua aplicação passa a processar dados já recebidos não é contabilizado. Para cancelamento e timeouts, veja Programação Assíncrona.

Transmitir dados binários

O AdventureWorks armazena o conteúdo dos ficheiros na coluna anulável varbinary(max)Production.Document.Document. Este exemplo copia um valor para um ficheiro com um buffer limitado:

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

Para texto, use GetTextReader e copie para o TextWriter. Para XML, use GetXmlReader e processe nós XML sem converter o valor completo em string.

Transmitir dados para uma base de dados SQL

Defina Value para um objeto de streaming e escolha um tipo de SQL compatível:

Tipo de valor Tipos de parâmetros compatíveis
Stream Binary, VarBinary, e o tipo legado Image
TextReader Char, NChar, VarChar, NVarChar, os tipos legados Text e NText, Xml e Json
XmlReader Char, NChar, VarChar, NVarChar, os tipos legados Text e NText, Xml e Json

Defina Size explicitamente. Size limita o montante transferido. Utilize -1 para um parâmetro (max) quando quiser enviar o código-fonte completo.

Para transferência de memória limitada, use VarBinary, VarChar, ou NVarChar com Size = -1, ou use Xml ou Json. Microsoft.Data.SqlClient coloca os dados de origem em memória intermédia num array de bytes ou cadeia antes da execução do comando para tipos de caráteres não (max) e binários e os tipos legados Image, Text e NText.

Transmitir em fluxo um valor de parâmetro

Este exemplo envia um ficheiro através de um parâmetro varbinary(max) e devolve o número de bytes recebidos. Não armazena o valor:

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

Mantenha o objeto de entrada aberto até a execução do comando concluir. Passe um token de cancelamento para um método de execução assíncrono ou chame Cancel para solicitar o cancelamento da operação de streaming. O cancelamento é efetuado na medida do possível. A operação pode terminar antes de o cancelamento entrar em vigor.

Uma exceção no fluxo de origem ou no leitor provoca falha na execução do comando. Não tente novamente a partir do mesmo objeto a menos que consiga estabelecer a sua posição e a operação da base de dados seja segura para repetir.

Transmitir dados de uma base de dados SQL para outra

Este exemplo passa um fluxo de documento AdventureWorks diretamente para um parâmetro noutro SqlConnection aberto chamado destinationConnection. O comando de exemplo reporta o tamanho recebido. Substitua-o pelo comando parametrizado de inserção ou atualização da base de dados de destino:

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

Os comandos de origem e destino não partilham uma transação. Se a sua aplicação exigir trabalho atómico coordenado entre servidores, projete e teste essa fronteira de transação separadamente.