如何通过GetChanges()返回的DataTable访问原DataTable对应行以实现单条数据撤销?
如何通过GetChanges()返回的DataTable访问原DataTable对应行以实现单条数据撤销?
这个问题我之前也踩过坑,太懂你的无奈了——GetChanges返回的是原表的副本表,直接对副本里的行调用RejectChanges完全不影响原表;而且用Rows.Find找原行还找不到,因为默认查找的是Current版本的行,而标记为Deleted的行在Current版本里相当于“隐身”了。
咱们一步步解决这个问题:
核心问题拆解
- 副本行不关联原表:GetChanges(DataRowState.Deleted)返回的DataTable里的行是原行的拷贝,对它做任何操作都只会修改副本,和原表的行没关系,所以你之前的
r.RejectChanges()根本不会作用到原数据上。 - Find方法的局限性:Rows.Find默认是基于Current版本的值查找行,但Deleted行的Current版本已经被标记为删除,所以用Original版本的主键值去Find,自然找不到对应的行。
正确的解决方案
我们需要在原表中精准定位到标记为Deleted的行,这里有两种可靠的方式:
方法一:使用DataTable.Select(高效适合大量数据)
利用Select方法可以指定查找的行状态,直接筛选出原表中Deleted状态且主键匹配的行:
DataTable originalTable = GetTable(); DataTable del = originalTable.GetChanges(DataRowState.Deleted); if (del != null) { foreach (DataRow r in del.Rows) { // 获取副本行中原版本的主键值(这是删除前行的主键) var originalId = r["id", DataRowVersion.Original]; // 注意:如果主键是字符串类型,要给值加单引号,比如 $"id = '{originalId}'" string filter = $"id = {originalId}"; // 在原表中筛选Deleted状态、主键匹配的行 DataRow[] foundRows = originalTable.Select(filter, "", DataViewRowState.Deleted); if (foundRows.Length > 0) { // 对原表的行执行撤销删除操作 foundRows[0].RejectChanges(); } } }
方法二:使用LINQ遍历筛选(更直观,适合小数据量)
如果你的数据量不大,用LINQ遍历原表的行,直接匹配状态和原版本主键值也很方便:
DataTable originalTable = GetTable(); DataTable del = originalTable.GetChanges(DataRowState.Deleted); if (del != null) { foreach (DataRow r in del.Rows) { var originalId = r["id", DataRowVersion.Original]; // 从原表的所有行中找到Deleted状态、原版本主键匹配的行 DataRow originalRow = originalTable.Rows.Cast<DataRow>() .FirstOrDefault(row => row.RowState == DataRowState.Deleted && row["id", DataRowVersion.Original].Equals(originalId)); originalRow?.RejectChanges(); } }
为什么这两种方法能生效?
原表的Rows集合其实包含所有状态的行,包括Deleted,只是默认的DataView会隐藏Deleted行,我们通过指定DataViewRowState.Deleted或者直接判断RowState,就能把这些“隐身”的行找出来;同时我们用的是原版本的主键值来匹配,这和删除前的行主键一致,所以能精准定位到对应的原行。
内容来源于stack exchange
相关产品推荐
相关产品推荐

