SqlClient ストリーミング サポート

Microsoft.Data.SqlClient のストリーミング メソッドを使用すると、値全体を最初にアプリケーション メモリに読み込むことなく、大きな値を処理できます。 ストリーミングはバイナリファイル、大きなテキスト、XML、JSON、サーバー間転送に便利です。

ストリーミングは値をシーク可能にしたり、リーダーの並行操作を安全にしたりしません。 接続、コマンド、リーダー、ソースストリーム、宛先オブジェクトは転送が完了するまで存続しなければなりません。

この記事の例は、名前が SqlConnection の開いている connection がAdventureWorks のサンプル データベースを対象としていることを前提としています。

C#の例は using Microsoft.Data.SqlClient;を必要とします。 SqlDbTypeやCommandBehaviorを使う例もusing System.Data;が必要です。

SQLデータベースからデータをストリーミングする

CommandBehavior.SequentialAccess を指定してコマンドを実行し、列を順序通りに読み取ります。 シーケンシャルアクセスでは、列を越えた後は同じ行の前の列を読まないでください。

以下の SqlDataReader 方法を活用してください:

メソッド 結果 典型的なSQLデータ
GetStream Stream バイナリ、varbinary、image、ユーザー定義型 (UDT) データ
GetTextReader TextReader 文字およびJSONデータ
GetXmlReader XmlReader xml

nullable columnのストリームを要求する前に IsDBNull を呼び出してください。 互換性のないSQL型で使用すると、ストリーミングゲッターは例外をスローする。 次の列を読む前に、行を進める、または次の結果に移動する前に、現在のストリームを消費または廃棄してください。

GetStream 暗号化されたカラムには対応していません。 GetTextReader 暗号化された列への順次アクセスをサポートしていません。

CommandTimeout は、Microsoft.Data.SqlClient が実行および読み取り中にネットワーク パケットを待機している間に適用されます。 アプリケーションが既に受信したデータの処理に費やす時間はカウントされません。 キャンセルやタイムアウトについては、 非同期プログラミングを参照してください。

バイナリ データのストリーム

AdventureWorksはファイルの内容をnullable varbinary(max)Production.Document.Document 列に格納します。 この例は、境界付きバッファを使ってファイルに1つの値をコピーします:

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

テキストは GetTextReader を使い、 TextWriterにコピーしてください。 XMLの場合は、 GetXmlReader を使い、XMLノードを文字列に変換せずに処理してください。

データをSQLデータベースにストリーミングする

Valueをストリーミングオブジェクトに設定し、互換性のあるSQL型を選択してください:

値タイプ 対応するパラメーター型
Stream Binary、 VarBinary、そしてレガシー Image 型
TextReader Char、 NChar、 VarChar、 NVarChar、レガシー Text および NText タイプ、 Xml、および Json
XmlReader Char、 NChar、 VarChar、 NVarChar、レガシー Text および NText タイプ、 Xml、および Json

Size明示的に設定します。 Size 転送される量を制限します。 ソース全体を送信したい場合は、(max) パラメーターに -1 を使用します。

有界メモリ転送の場合は、 VarBinary、 VarChar、または NVarChar を Size = -1と組み合わせ、または Xml または Jsonを使用します。 Microsoft.Data.SqlClientは、非(max)文字型やバイナリ型、レガシーのImage、Text、NText型に対して、実行前にソースをバイト配列または文字列にバッファリングします。

パラメータ値をストリーミングする

この例では、ファイルを varbinary(max) パラメーターを介して送信し、受信したバイト数を返します。 値は保存されません:

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

コマンド実行が完了するまで入力オブジェクトを開いたままにしてください。 キャンセルトークンを非同期実行メソッドに渡すか、ストリーミング操作のキャンセルを要求するために Cancel を呼び出します。 キャンセルはベストエフォートです。 処理はキャンセルが効力を発揮する前に完了するかもしれません。

ソース ストリームからの例外、またはリーダーのエラーにより、コマンドの実行が中断されました。 同じオブジェクトの位置を特定できてデータベース操作が安全に繰り返しられる場合を除き、同じオブジェクトから再試行しないでください。

あるSQLデータベースから別のSQLデータベースへデータをストリーミングする

この例では、AdventureWorks のドキュメント ストリームを、destinationConnection という名前の別の開いている SqlConnection のパラメーターに直接渡します。 サンプルコマンドは受信長を報告します。 これを宛先データベースのパラメータ化された挿入または更新コマンドに置き換えます:

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

送信元と宛先コマンドはトランザクションを共有していません。 アプリケーションがサーバー間で協調した原子作業を必要とする場合は、そのトランザクション境界を別途設計・テストしてください。