Compatibilidad con streaming de SqlClient

Utiliza los métodos de streaming de Microsoft.Data.SqlClient para procesar valores grandes sin cargar primero el valor completo en la memoria de la aplicación. El streaming es útil para archivos binarios, texto grande, XML, JSON y transferencia de servidor a servidor.

El streaming no hace que el valor sea posicionable ni hace que las operaciones concurrentes sobre un lector sean seguras. La conexión, el comando, el lector, el flujo fuente y el objeto destino deben permanecer activos hasta que termine la transferencia.

Los ejemplos de este artículo asumen que un connection abierto denominado SqlConnection apunta a la base de datos de ejemplo de AdventureWorks.

Los ejemplos de C# requieren using Microsoft.Data.SqlClient;. Ejemplos que usan SqlDbType o CommandBehavior también requieren using System.Data;.

Transmitir datos desde una base de datos SQL

Ejecuta el comando con CommandBehavior.SequentialAccess y lee las columnas en orden ordinal. Con el acceso secuencial, después de avanzar más allá de una columna, no leas una columna anterior en la misma fila.

Utiliza estos SqlDataReader métodos:

Método Resultado Datos SQL típicos
GetStream Stream binario, varbinary, image y datos de tipo definido por el usuario (UDT)
GetTextReader TextReader Datos de caracteres y JSON
GetXmlReader XmlReader xml

Llame a IsDBNull antes de solicitar un flujo para una columna anulable. Un getter de streaming usado con un tipo SQL incompatible genera una excepción. Consuma o elimine el flujo actual antes de leer otra columna, avanzar la fila o pasar al siguiente resultado.

GetStream no es compatible con columnas cifradas. GetTextReader no soporta acceso secuencial a una columna cifrada.

CommandTimeoutse aplica mientras Microsoft. Data.SqlClient espera los paquetes de red durante la ejecución y lectura. El tiempo que tu aplicación dedica a procesar datos ya recibidos no se cuenta. Para cancelaciones y tiempos de espera, véase Programación asíncrona.

Transmitir datos binarios

AdventureWorks almacena el contenido de archivos en la columna que acepta valores NULL varbinary(max)Production.Document.Document. Este ejemplo copia un valor a un archivo con un búfer acotado:

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 el texto, usa GetTextReader y copia a un TextWriter. Para XML, usa GetXmlReader y procesa nodos XML sin convertir el valor completo en cadena.

Transmitir datos a una base de datos SQL

Configura Value como un objeto de streaming y elige un tipo SQL compatible:

Tipo de valor Tipos de parámetros compatibles
Stream Binary, VarBinary, y el tipo legado Image
TextReader Char, NChar, VarChar, NVarChar, los tipos heredados Text y NText, Xml y Json
XmlReader Char, NChar, VarChar, NVarChar, los tipos heredados Text y NText, Xml, y Json

Establezca Size explícitamente. Size limita la cantidad transferida. Usa -1 para un parámetro (max) cuando quieras enviar el código fuente completo.

Para la transferencia de memoria acotada, se utiliza VarBinary, VarChar, o NVarChar con Size = -1, o se usa Xml o Json. Microsoft.Data.SqlClient almacena en búfer el origen en una matriz de bytes o cadena antes de la ejecución para tipos no de caracteres ni binarios y los tipos heredados Image, Text y NText.

Transmitir un valor de parámetro

Este ejemplo envía un archivo a través de un parámetro varbinary(max) y devuelve el número de bytes recibidos. No almacena el 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");

Mantén el objeto de entrada abierto hasta que se complete la ejecución del comando. Pasa un token de cancelación a un método de ejecución asíncrono o llama a Cancel para solicitar la cancelación de la operación de streaming. La cancelación se realiza en la medida de lo posible. La operación podría terminar antes de que la cancelación entre en vigor.

Una excepción en el flujo de origen o en el lector provoca un error en la ejecución del comando. No intentes de nuevo desde el mismo objeto a menos que puedas establecer su posición y la operación de la base de datos sea segura para repetir.

Transmitir datos de una base de datos SQL a otra

Este ejemplo pasa un flujo de documento de AdventureWorks directamente a un parámetro en otro SqlConnection abierto llamado destinationConnection. El comando de ejemplo informa la longitud recibida. Sustitúyalo por el comando parametrizado de inserción o actualización de la base de datos 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());
}

Los comandos de origen y destino no comparten una transacción. Si tu aplicación requiere trabajo atómico coordinado entre servidores, diseña y prueba ese límite de transacción por separado.