C# DataAdapter 使用指南:填充、更新与事件详解
本文详细介绍 C# 中 DataAdapter(以 SqlDataAdapter 为例)的使用方式,涵盖数据填充的多种策略(包括多结果集处理和分页)、基于 DataTable 映射的数据更新机制、SqlCommandBuilder 的自动命令生成以及行级更新事件的捕获。
一、填充数据(Fill)
DataAdapter 主要通过 Fill 方法将数据源中的结果集加载到 DataSet 中。在执行过程中,如果连接未打开,它会自动打开并在完成后关闭;若连接已打开,则需要手动管理其状态。
第1步:基础填充与表映射
目的:将查询结果加载到 DataSet 并根据需要重命名内存中的表名和列名。
代码示例:
DataSet ds = new DataSet();
// 查询包含两个结果集的 SQL
SqlCommand cmd = new SqlCommand("select * from Catogories;select * from Customers", conn);
SqlDataAdapter da = new SqlDataAdapter(cmd);
// 配置表映射:第一个参数是数据源返回的第一个表的默认名称(Table),第二个参数是 DataSet 中创建的表名
DataTableMapping map = da.TableMappings.Add("Table", "Customer");
map.ColumnMappings.Add("Name", "CustomerName"); // 列重映射
// 处理模式冲突:AddWithKey 会添加主键信息
da.MissingSchemaAction = MissingSchemaAction.AddWithKey;
// 填充数据:新增的 DataTable 默认为 "Table",后续为 "Table1" 等
da.Fill(ds);
da.Fill(ds, "Customer"); // 显式指定目标表名
// 分页填充:从索引0开始,取10000行
da.Fill(ds, 0, 10000, "Customer");
// 仅填充架构信息(列结构),不加载数据
da.FillSchema(ds, System.Data.SchemaType.Source, "Customer");
第2步:处理多条 SQL 语句的结果集
当 SQL 命令包含分号分隔的多条语句时,DataAdapter 会为每个结果集创建一个新的 DataTable。以下是三种常见的处理方式:
方案1:使用不同的 CommandText 分别填充
每次填充前修改 Command 文本,或使用不同的 DataAdapter 实例。需注意区分传入的表名字符串。
SqlCommand cmd = new SqlCommand("select * from Catogories", conn);
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(ds, "Catogories");
cmd.CommandText = "select * from Customers";
SqlDataAdapter da2 = new SqlDataAdapter(cmd);
da2.Fill(ds, "Customer");
方案2:复用 DataAdapter 更改 Text
通过修改同一个 DataAdapter 的 SelectCommand.CommandText 属性来依次填充不同数据。
SqlCommand cmd = new SqlCommand("select * from Catogories", conn);
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(ds, "Catogories");
da.SelectCommand.CommandText = "select * from Customers";
da.Fill(ds, "Customer");
方案3:一次性获取多结果集(推荐)
在一条 SQL 中使用分号,DataAdapter 会自动按顺序将结果集填入 DataSet 的不同表中(表名默认为 Table, Table1 等,可通过 TableMappings 定制)。
SqlCommand cmd = new SqlCommand("select * from Catogories;select * from Customers", conn);
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(ds);
二、更新数据(Update)
当内存中的 DataSet 发生改变后,可以通过 Update 方法将这些变更同步回数据库。通常需要配合 SqlCommandBuilder 自动生成更新指令。
第3步:增量更新与提交
入口:DataSet 变更记录与 DataAdapter。
动作:
- 调用
GetChanges()获取所有修改过的行。 - 实例化
SqlCommandBuilder,这会根据 SelectCommand 的 SQL 自动生成 Insert、Update 和 Delete 命令(前提是 Select SQL 必须包含主键或唯一约束的列)。 - 调用
Update()执行回写,该方法会根据行的DataRowState自动选择对应的 SQL 命令。 - 最后调用
AcceptChanges()刷新内存状态。
DataSet dsChanged = ds.GetChanges();
if (dsChanged != null)
{
SqlCommandBuilder builder = new SqlCommandBuilder(da);
// 注意:Update 检测每条记录的行状态
da.Update(ds, "Author");
ds.AcceptChanges(); // 将 Added/Modified 标记为 Unchanged,移除 Deleted
}
关键方法对比:
AcceptChanges:提交更改。Added 和 Modified 行变为 Unchanged,Deleted 行被移除。RejectChanges:撤销更改。Added 行被删除,Modified/Deleted 行恢复初始状态。
三、自定义 SQL 参数与映射
在使用 OleDb 或其他不支持自动生成的适配器时,需手动定义 Command。此时需关注 UpdatedRowSource 属性对输出参数的处理。
第4步:配置参数化插入
OleDbDataAdapter da = new OleDbDataAdapter();
OleDbCommand cmd = new OleDbCommand(
"insert into [A_Emp_Dept_20190522](empname,line) values (?,?)", conn);
// 添加输入参数
OleDbParameter param1 = new OleDbParameter("empname", OleDbType.Char, 11, ParameterDirection.Input, false, 0, 0, "empname", DataRowVersion.Current, null);
OleDbParameter param2 = new OleDbParameter("line", OleDbType.Char, 11, ParameterDirection.Input, false, 0, 0, "line", DataRowVersion.Current, null);
cmd.Parameters.Add(param1);
cmd.Parameters.Add(param2);
da.InsertCommand = cmd;
// UpdatedRowSource 控制输出如何映射回 DataSet
// da.InsertCommand.UpdatedRowSource = UpdateRowSource.OutputParameters;
DataSet ds = new DataSet();
ds.Tables[0].Rows.Add(new object[] { "222", "FBd" });
da.Update(ds);
UpdatedRowSource 选项说明
Both:允许映射输出参数和返回的第一行结果集。(默认值)FirstReturnedRecord:仅映射返回结果集的第一行。OutputParameters:仅映射输出参数。None:忽略所有返回值和参数。
四、处理更新事件
在进行批量更新时,建议订阅 DataAdapter 的事件以进行日志记录或错误处理。
第5步:监听 RowUpdating 与 RowUpdated
RowUpdating 发生在数据库命令执行前,可用于取消当前操作或检查参数。
RowUpdated 发生在命令执行后,通常用于检查是否发生异常(Errors 属性)或确认影响的行数(RecordsAffected)。
SqlRowUpdatedEventArgs 常用属性:
Error/Errors:获取执行过程中的异常信息。RecordsAffected:获取受影响(增删改)的行数。Row:获取正在处理的 DataRow。StatementType:当前执行的语句类型(Insert, Update, Delete)。Status:获取命令执行的状态(如 Continue, SkipCurrentRow 等)。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









