用 SqlBatch 來將一組 SqlBatchCommand 有序的物件集合傳送到一個開的 SqlConnection。 每個批次指令都有自己的指令文字、命令類型、參數、指令行為及受影響的資料列數結果。
當應用程式有明確區分的指令要一起傳送時,請使用 SqlBatch 當工作只有單一陳述式或儲存程序時,可以使用一個參數化 SqlCommand。 對於大型列集,可以使用 SqlBulkCopy 或使用表值參數。
本文範例假設一個名為 connection 的開啟 SqlConnection 以 AdventureWorks 範例資料庫 為目標。
C# 範例需要 using Microsoft.Data.SqlClient;。 使用 SqlDbType 或 CommandBehavior 的範例也需要 using System.Data;。 使用 DbDataReader 的例子需要 using System.Data.Common;。
版本與平台支援
這些範例針對 Microsoft.Data.SqlClient 7.1.0。 選擇你的應用程式適用且受 Microsoft.Data.SqlClient support lifecycle 支援的套件版本與目標架構。
在現代 .NET 上,SqlBatch衍生自DbBatch。 Microsoft.Data.SqlClient 也在其 .NET Framework 與 .NET Standard 2.0 套件資產中提供 SqlBatch。 公開 API 介面會因目標框架而異,因此請為您發佈的每個目標編譯應用程式。
建立批次
建立一個開連線的批次,然後依執行順序新增指令:
using var batch = new SqlBatch(connection);
batch.BatchCommands.Add(new SqlBatchCommand("""
SELECT TOP (3) ProductID, Name
FROM Production.Product
ORDER BY ProductID;
"""));
batch.BatchCommands.Add(new SqlBatchCommand("""
SELECT TOP (3) SalesOrderID, OrderDate
FROM Sales.SalesOrderHeader
ORDER BY SalesOrderID;
"""));
using DbDataReader reader = batch.ExecuteReader();
do
{
while (reader.Read())
{
Console.WriteLine($"{reader.GetValue(0)} | {reader.GetValue(1)}");
}
}
while (reader.NextResult());
CommandType.Text 是預設值。 將儲存程序的批次指令設為 CommandType.StoredProcedure。 設定 CommandType.TableDirect 會擲回 ArgumentOutOfRangeException。
為批次指令新增參數
參數屬於個別項目 SqlBatchCommand。 兩個批次指令中的相同佔位名稱不會共享值:
using var batch = new SqlBatch(connection);
var products = new SqlBatchCommand("""
SELECT ProductID, Name
FROM Production.Product
WHERE ProductSubcategoryID = @id
ORDER BY ProductID;
""");
products.Parameters.Add("@id", SqlDbType.Int).Value = 2;
batch.BatchCommands.Add(products);
var orders = new SqlBatchCommand("""
SELECT SalesOrderID, OrderDate
FROM Sales.SalesOrderHeader
WHERE SalesPersonID = @id
ORDER BY SalesOrderID;
""");
orders.Parameters.Add("@id", SqlDbType.Int).Value = 279;
batch.BatchCommands.Add(orders);
using DbDataReader reader = batch.ExecuteReader();
設定每個參數的 SQL 類型,以及資料庫定義中適用的大小、精確度和小數位數。 如需相關資訊,請參閱設定參數。
執行資料變更指令
當批次變更資料且不需要結果列時,請使用 ExecuteNonQuery 或 ExecuteNonQueryAsync。 回傳的值是當有列數可用時受影響的總列數。 執行後,每個指令會 RecordsAffected 報告自己的計數。
以下範例回滾其變更:
using SqlTransaction transaction = connection.BeginTransaction();
using var batch = new SqlBatch(connection, transaction);
var firstUpdate = new SqlBatchCommand("""
UPDATE Sales.SalesOrderHeader
SET Status = @status
WHERE SalesOrderID = @salesOrderId;
""");
firstUpdate.Parameters.Add("@status", SqlDbType.TinyInt).Value = 2;
firstUpdate.Parameters.Add("@salesOrderId", SqlDbType.Int).Value = 43659;
batch.BatchCommands.Add(firstUpdate);
var secondUpdate = new SqlBatchCommand("""
UPDATE Sales.SalesOrderHeader
SET Status = @status
WHERE SalesOrderID = @salesOrderId;
""");
secondUpdate.Parameters.Add("@status", SqlDbType.TinyInt).Value = 2;
secondUpdate.Parameters.Add("@salesOrderId", SqlDbType.Int).Value = 43660;
batch.BatchCommands.Add(secondUpdate);
int totalRows = batch.ExecuteNonQuery();
Console.WriteLine($"Total rows: {totalRows}");
foreach (SqlBatchCommand command in batch.BatchCommands)
{
Console.WriteLine(command.RecordsAffected);
}
transaction.Rollback();
查看多個結果
當任何指令傳回資料列時,請使用 ExecuteReader 或 ExecuteReaderAsync。 讀卡機會以批次指令順序輸出結果。 呼叫NextResult或NextResultAsync,直到它傳回false。
指令可以產生任何列集、單一列集,或多列集。 不要以為一個 NextResult 呼叫對應一個 SqlBatchCommand。
使用命令行為
當讀取器需要順序存取或必須關閉連線時,將批次層級 CommandBehavior 傳遞至 ExecuteReader 或 ExecuteReaderAsync:
using var batch = new SqlBatch(connection);
batch.BatchCommands.Add(new SqlBatchCommand("""
SELECT DocumentSummary
FROM Production.Document
WHERE DocumentSummary IS NOT NULL;
"""));
using DbDataReader reader =
batch.ExecuteReader(CommandBehavior.SequentialAccess);
char[] buffer = new char[4096];
while (reader.Read())
{
using TextReader summary = reader.GetTextReader(0);
int count;
while ((count = summary.Read(buffer, 0, buffer.Length)) > 0)
{
Console.Out.Write(buffer, 0, count);
}
Console.WriteLine();
}
批次層級的多載僅接受 CommandBehavior.Default、CommandBehavior.SequentialAccess、CommandBehavior.CloseConnection及這些值的組合。 其他有效值,如 SingleRow、SchemaOnly 和 KeyInfo,在傳遞給批次層級多載版本時會拋出 ArgumentOutOfRangeException。 批次層級的行為多載自 Microsoft.Data.SqlClient 7.1 起可用。
在個別指令上設定 CommandBehavior,以選擇如 SingleRow、SchemaOnly 和 KeyInfo 等選項。 使用批次層級的多載 和 SequentialAccess 和 CloseConnection。
使用事務
SqlBatch 不會使其命令具備原子性。 當所有指令都必須一起認可或回復時,請使用明確的交易作業。 此範例回滾,使 AdventureWorks 保持不變:
using SqlTransaction transaction = connection.BeginTransaction();
using var batch = new SqlBatch(connection, transaction);
batch.BatchCommands.Add(new SqlBatchCommand("""
UPDATE Sales.SalesOrderHeader
SET Status = 2
WHERE SalesOrderID = 43659;
"""));
batch.BatchCommands.Add(new SqlBatchCommand("""
UPDATE Sales.SalesOrderHeader
SET Status = 2
WHERE SalesOrderID = 43660;
"""));
batch.ExecuteNonQuery();
transaction.Rollback();
只有在應用程式接受完整批次時,才呼叫Rollback而非Commit。 在執行前指派事務。 交易與批次必須使用相同的連線。 SqlClient 未覆寫非同步交易方法,因此 BeginTransactionAsync、CommitAsync 和 RollbackAsync 會以同步方式完成。 欲了解更多資訊,請參閱 使用基於任務的 SQL 事務方法。
處理逾時、取消與錯誤
將批次的 Timeout 設定為幾秒。 批次組在您設定連線後,會使用該連線的 Command Timeout 設定。 否則,預設時間是 30 秒。 值 0 沒有時間限制,負值則擲出 ArgumentException。 當呼叫者需要停止等待或呼叫 Cancel時,將取消標記傳遞給非同步執行方法。 取消僅保證盡力處理。 作業可能在取消生效前完成。
執行失敗時,捕捉 SqlException 並檢查其 Errors 集合中的每一項。
BatchCommand 會在可取得該資訊時識別相關的 SqlBatchCommand:
using var batch = new SqlBatch(connection);
batch.BatchCommands.Add(new SqlBatchCommand(
"SELECT TOP (1) ProductID FROM Production.Product;"));
batch.BatchCommands.Add(new SqlBatchCommand("SELECT 1 / 0;"));
try
{
using DbDataReader reader = batch.ExecuteReader();
do
{
while (reader.Read())
{
Console.WriteLine(reader.GetValue(0));
}
}
while (reader.NextResult());
}
catch (SqlException exception)
{
Console.WriteLine(
exception.BatchCommand is null
? "The batch failed."
: $"Failed command: {exception.BatchCommand.CommandText}");
}
Reader 的執行是漸進式的。 當 Read 或 NextResult 前進到該指令時,較後的批次指令可能會出現錯誤,因此請在 try 區塊內處理讀取器。
不要記錄包含憑證、權杖或敏感資料的參數值。 關於錯誤欄位與資訊訊息,請參見 處理錯誤與訊息。