SqlClient 串流支援

使用 Microsoft.Data.SqlClient 串流方法可處理大型值,而無需先將完整值載入應用程式記憶體。 串流對於二進位檔案、大型文字內容、XML、JSON 以及伺服器間傳輸非常有用。

串流並不會讓值變得可定位,也不會讓對讀取器進行並行操作變得安全。 連線、指令、讀取器、來源串流與目的物件必須保持存活直到傳輸完成。

本文範例假設名為 connection 的已開啟 SqlConnection 以 AdventureWorks 範例資料庫為目標。

C# 範例需要 using Microsoft.Data.SqlClient;。 使用 SqlDbType 或 CommandBehavior 的範例也需要 using System.Data;。

從 SQL 資料庫串流資料

執行指令 CommandBehavior.SequentialAccess,並依欄位序位順序讀取欄位。 使用順序存取時,當你越過一欄後,不要再讀同一列的前一欄。

請使用以下 SqlDataReader 方法:

方法 結果 典型的 SQL 資料
GetStream Stream binary、varbinary、image及使用者定義型別(UDT)資料
GetTextReader TextReader 字元與 JSON 資料
GetXmlReader XmlReader xml

在請求可空欄位的串流前先呼叫 IsDBNull 。 串流獲取器搭配不相容的 SQL 類型會擲出例外。 在讀取下一欄、推進該列或進入下一個結果前,先消耗或丟棄當前串流。

GetStream 不支援加密欄位。 GetTextReader 不支援對加密欄位的依序存取。

CommandTimeout 適用於 Microsoft.Data.SqlClient 在執行和讀取期間等待網路封包時。 你的應用程式處理已收到資料的時間不會被計算。 關於取消與逾時,請參見 非同步程式設計。

串流二進位資料

AdventureWorks 將檔案內容存放在可為 Null 的 varbinary(max)Production.Document.Document 欄位中。 此範例使用有界緩衝區將一個值複製到檔案:

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。

對於有界記憶體傳輸,請搭配 Size = -1 使用 VarBinary、VarChar 或 NVarChar,或使用 Json 或 Xml。 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 資料庫串流到另一個

此範例將 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());
}

來源和目的地指令不會共用事務。 如果你的應用程式需要跨伺服器協調原子作業,請分別設計並測試該交易邊界。