使用 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());
}
來源和目的地指令不會共用事務。 如果你的應用程式需要跨伺服器協調原子作業,請分別設計並測試該交易邊界。