SqlDataAdapter 封裝 SelectCommand、InsertCommand、UpdateCommand、DeleteCommand,負責 Fill(資料源 → DataSet)與 Update(DataSet → 資料源)雙向同步。本文涵蓋三種建構方式、TableMapping、連線管理、重複 Fill 陷阱與最佳實務。
// 1. 連接字串 + SQL 字串(簡單但每次新建連線)
var da1 = new SqlDataAdapter("SELECT * FROM Customers", connStr);
// 2. SQL 字串 + SqlConnection 物件(可重用連線)
using (var conn = new SqlConnection(connStr)) {
var da2 = new SqlDataAdapter("SELECT * FROM Customers", conn);
}
// 3. SqlCommand 物件(最靈活,可預設參數、交易)
using (var conn = new SqlConnection(connStr)) {
var cmd = new SqlCommand("SELECT * FROM Customers WHERE City = @City", conn);
cmd.Parameters.AddWithValue("@City", "London");
var da3 = new SqlDataAdapter(cmd);
}
建議:方式 2/3 可共用 SqlConnection,減少連線池壓力。
Fill 會自動開啟、查詢、關閉;若已開啟,則保持開啟狀態(開發者需自行關閉)。Table、Table1... 可用 da.TableMappings.Add("Table", "Customers") 或 da.Fill(ds, "Customers") 指定名稱。da.Fill(dt) 比填 DataSet 再取 Table 更簡潔。// ❌ 錯誤:兩次 Fill 觸發兩次 Open/Close
daCustomers.Fill(ds, "Customers");
daOrders.Fill(ds, "Orders");
// ✅ 正確:手動控制連線週期
conn.Open();
daCustomers.Fill(ds, "Customers");
daOrders.Fill(ds, "Orders");
conn.Close();
批次填充多個 DataAdapter 時,**務必手動 Open/Close 共用連線**,避免頻繁連線池操作。
da.Fill(ds, "Customers"); // 第 1 次
// ... 業務邏輯 ...
da.Fill(ds, "Customers"); // 第 2 次:資料會**累加**(重複),且已刪除的列不會移除
解決方案:再次 Fill 前先清除舊資料:
ds.Tables["Customers"].Clear(); // 或 ds.Tables["Customers"].Rows.Clear()
da.Fill(ds, "Customers");
若表有主鍵,Fill 會依主鍵合併(Upsert),但仍無法偵測資料源端已刪除的列。
da.Update(ds, "Table") 時,掃描 DataTable 所有列的 RowState。Added/Modified/Deleted 列執行對應 Command。var da = new SqlDataAdapter("SELECT * FROM Customers", conn);
var cb = new SqlCommandBuilder(da); // 自動產生 Insert/Update/DeleteCommand
da.UpdateCommand = cb.GetUpdateCommand();
// 現在可直接 da.Update(ds) 同步修改
限制:複雜 Join、無主鍵、預存程序場景需手寫 Command。
using (var conn = new SqlConnection(connStr))
using (var da = new SqlDataAdapter("SELECT * FROM Customers WHERE Country = 'UK'", conn))
using (var cb = new SqlCommandBuilder(da)) {
var dt = new DataTable();
da.Fill(dt);
// 業務層修改 dt.Rows...
dt.Rows[0]["ContactName"] = "New Name";
dt.Rows.Add(dt.NewRow() { ["CustomerID"] = "NEW01", ["Country"] = "UK" });
da.Update(dt); // 批次同步
}
SqlCommand + SqlConnection 建構(方式 3)。Open() 共用連線。SqlCommandBuilder 減少樣板碼。SqlCommand.ExecuteNonQuery/Reader/Scalar,別用 DataAdapter。遵循這些原則,SqlDataAdapter 將成為穩定高效的離線資料同步利器。