SqlDataAdapter 是 ADO.NET 中連線層與離線層(DataSet/DataTable)的橋樑。核心職責:
| 屬性 | 對應操作 | 典型 SQL |
|---|---|---|
| SelectCommand | 查詢 | SELECT * FROM Users |
| InsertCommand | 新增 | INSERT INTO Users (...) VALUES (...) |
| UpdateCommand | 修改 | UPDATE Users SET ... WHERE Id = @Id |
| DeleteCommand | 刪除 | DELETE FROM Users WHERE Id = @Id |
若僅需查詢,只設定SelectCommand即可;啟用Update時四大 Command 皆需配置(可用SqlCommandBuilder自動產生)。
// 1. SQL 字串 + 連線字串(最簡單)
var da = new SqlDataAdapter("SELECT * FROM Users", "Server=.;Database=Test;Integrated Security=true;");
// 2. SQL 字串 + SqlConnection 物件
using var conn = new SqlConnection(connStr);
var da2 = new SqlDataAdapter("SELECT * FROM Users", conn);
// 3. 完整 SqlCommand 物件(參數化、預存程序)
var cmd = new SqlCommand("GetUsersByRole", conn) { CommandType = CommandType.StoredProcedure };
cmd.Parameters.AddWithValue("@Role", "Admin");
var da3 = new SqlDataAdapter(cmd);
// 基本填充 → 自動建立名為 "Table" 的 DataTable
var ds = new DataSet();
da.Fill(ds); // ds.Tables["Table"]
// 指定 DataTable 名稱
da.Fill(ds, "Users"); // ds.Tables["Users"]
// 填充現有 DataTable
var dt = new DataTable();
da.Fill(dt);
// 分頁填充(注意:仍會查詢全表,效能不佳,大數據請用 SQL 分頁)
da.Fill(ds, startRecord: 0, maxRecords: 20, "UsersPage1"); // 前 20 筆
Fill 執行前若連線關閉 → 自動開啟 → 執行完畢自動關閉Fill 時,建議手動 conn.Open() → 多次 Fill → conn.Close(),避免重複開關// 表名映射:資料庫結果集 "Table" → DataTable "Employees"
da.TableMappings.Add("Table", "Employees");
// 欄位映射
var tableMap = da.TableMappings["Employees"]; // 取得 DataTableMapping
tableMap.ColumnMappings.Add("EmpID", "EmployeeId");
tableMap.ColumnMappings.Add("LName", "LastName");
tableMap.ColumnMappings.Add("FName", "FirstName");
// 批次加入
tableMap.ColumnMappings.AddRange(new[]
{
new DataColumnMapping("HireDate", "HireDate"),
new DataColumnMapping("Salary", "AnnualSalary")
});
| 列舉值 | 行為 |
|---|---|
| Passthrough(預設) | 無映射欄位照原名填入 DataSet |
| Ignore | 忽略無映射欄位 |
| Error | 拋出例外 |
da.MissingMappingAction = MissingMappingAction.Error; // 嚴格模式
// 1. 設定四大 Command(或用 SqlCommandBuilder 自動產生)
var builder = new SqlCommandBuilder(da); // 需 SelectCommand 為單表查詢且有主鍵
// builder 自動產生 Insert/Update/Delete Command
// 2. 修改 DataTable
dt.Rows[0]["LastName"] = "Wang"; // 修改
var newRow = dt.NewRow(); newRow["EmployeeId"] = 99; newRow["LastName"] = "New"; dt.Rows.Add(newRow); // 新增
dt.Rows[1].Delete(); // 標記刪除
// 3. 批次更新
int affected = da.Update(dt); // 回傳影響筆數
Console.WriteLine($"已同步 {affected} 筆變更");
| 陷阱 | 解決方案 |
|---|---|
| Fill 重複資料 | 更新前先 dt.Clear() 或 ds.Tables["Users"].Clear() |
| Update 失敗無錯誤訊息 | 檢查 RowUpdated 事件、SqlCommandBuilder 是否正確產生 WHERE 條件 |
| 大量資料 Fill 效能差 | 使用 SqlBulkCopy 匯入;分頁請在 SQL 層用 OFFSET/FETCH |
| 並發衝突 | 資料表加 RowVersion/Timestamp 欄位,啟用樂觀鎖 |
| 參數化查詢避免注入 | 永遠使用 Parameters.AddWithValue 或 Add |
Visual Studio 設計師可從資料庫產生 .xsd → 強型別 DataSet、DataTable、TableAdapter(內建 SqlDataAdapter),提供 IntelliSense、編譯期檢查與型別安全,企業級專案強力推薦。
SqlDataAdapter 是 ADO.NET 離線架構的核心,掌握 Fill/Update 雙向流、Command 設定、TableMappings 映射、連線管理,即可建構穩健的資料存取層。現代專案若使用 Entity Framework Core / Dapper,仍建議理解其底層運作原理,以便效能調校與疑難排解。