删除datatable的行后,出现“不能通过已删除的行访问该行的信息”的错误,即DeletedRowInaccessibleException

本文介绍如何正确使用 DataTable 的 Delete() 方法来删除行,并解释为何需要配合使用 AccepteChanges() 方法才能彻底删除行。此外还提供了两种替代方案:使用 RemoveAt() 或 Remove() 方法直接删除指定行。

本文转自:http://blog.163.com/luoliang1206@126/blog/static/408634752012593453245/

 

采用datatable.Rows[i].Delete()删除行后再访问该表时出现出现“不能通过已删除的行访问该行的信息”的错误。原因如下:

Delete()之后需要datatable.AccepteChanges()方法确认完全删除,因为Delete()只是将相应列的状态标志为删除,还可以通过datatable.RejectChanges()回滚,使该行取消删除。

所以如果要彻底删除datarow,需要Delete()和AccepteChanges()方法同时使用,或者采用datatable.Rows.RemoveAt(i)方法直接删除,其中i表示行索引,还有一个就是datatable.Rows.Remove(DataRow dr)删除指定行。

不过使用datatable.Rows.RemoveAt(i)要注意,如果连续使用datatable.Rows.RemoveAt(0);datatable.Rows.RemoveAt(1);这时并不是删除了原表中的0,1行,而是删除0行后,原来的1行就变成了0行,所以datatable.Rows.RemoveAt(1)实际删除的是原表的2行。

所以还是要慎用datatable.Rows.RemoveAt(i),若要删除多行,可以连续用Delete(),然后采用AccepteChanges()方法确认删除。


这个问题: > **“不能通过已删除访问该行信息”** 通常是由于你在 C# 中调用了 `DataTable.GetChanges()` 方法,并且在删除操作中使用了 `DataRow.Delete()` 方法,但你又试图从这些已经被标记为 `Deleted` 状态的中读取数据(例如 `guid_id`),从而导致异常。 --- ### ✅ 问题原因 当你调用: ```csharp DataTable changes = tb1.GetChanges(); ``` 如果 `tb1` 中存在已被调用 `.Delete()` 的,那么这些的状态是 `DataRowState.Deleted`。 如果你尝试访问这些的字段(如 `row["guid_id"]`),就会抛出: > **"Cannot access a row that has been deleted."** --- ### ✅ 解决方案 你需要从**原始 DataTable (`tb1`)** 中获取 `guid_id`,而不是从 `changes` 中获取。 --- ### ✅ 修改后的 C# 删除逻辑(修复错误) ```csharp private void SaveData() { try { System.Collections.Hashtable Para = new System.Collections.Hashtable(); DataTable tb1 = dgvGrid1.GetCommitTable().Copy(); // 原始数据表 // 判断是否为删除操作 bool isDeleteOnly = false; List<string> deletedGuids = new List<string>(); foreach (DataRow row in tb1.Rows) { if (row.RowState == DataRowState.Deleted) { // 注意:从原始(RowState.Deleted 的)中读取字段要用 Original 值 deletedGuids.Add(row["guid_id", DataRowVersion.Original].ToString()); } } if (deletedGuids.Count > 0) { isDeleteOnly = true; Para.Add("@str_delete", string.Join(",", deletedGuids)); // 传入要删除的 guid_id 列表 } else { DataTable changes = tb1.GetChanges(); if (changes != null && changes.Rows.Count > 0) { Para.Add("@user_name1", PettyCashBasic.Classes.PettyCashLogin.UserName); Para.Add("@tb1", PettyCashBasic.Classes.PettyCashConnection.GetTable101(changes)); } else { MessageBox.Show("没有需要保存的变更。"); return; } } if (PettyCashBasic.Classes.PettyCashConnection.DataUpdate("p_user_mapping_sav", Para)) { tb1.AcceptChanges(); GetGridData(); if (isDeleteOnly) MessageBox.Show("删除完成。"); else MessageBox.Show("保存结束。"); } } catch (Exception ex) { MessageBox.Show(ex.Message); } } ``` --- ### ✅ 修改后的 SQL 存储过程(支持批量删除 guid_id) ```sql USE [pettycash] GO ALTER PROCEDURE [dbo].[p_user_mapping_sav] ( @user_name1 nvarchar(50) = '', @tb1 diytbl readonly, @str_delete nvarchar(max) = '' -- 逗号分隔的 guid_id 列表 ) AS BEGIN SET NOCOUNT ON; -- 创建临时表#TT,用于插入/更新数据 SELECT ISNULL(f0, '') guid_id, ISNULL(f1, '') soft_name, ISNULL(f2, '') host_user, ISNULL(f3, '') card_name, ISNULL(f4, '') user_name, ISNULL(f5, '') version_id, CASE WHEN ISNULL(f6, '') = '' THEN CONVERT(nvarchar(50), GETDATE(), 112) ELSE ISNULL(f6, '') END AS begin_date, CASE WHEN ISNULL(f7, '') = '' THEN '20991231' ELSE ISNULL(f7, '') END AS end_date, ISNULL(f8, '') rem_01, ISNULL(f9, '') rem_02, ISNULL(f10, '') rem_03, GETDATE() AS ins_time, @user_name1 AS ins_user, GETDATE() AS upd_time, @user_name1 AS upd_user INTO #TT FROM @tb1; -- 删除逻辑:如果 @str_delete 不为空,则删除对应 guid_id 的记录 IF ISNULL(@str_delete, '') <> '' BEGIN -- 使用 STRING_SPLIT 解析逗号分隔的 guid_id 列表 DELETE u FROM user_mapping u INNER JOIN STRING_SPLIT(@str_delete, ',') s ON u.guid_id = s.value END ELSE BEGIN -- 插入新纪录 INSERT INTO user_mapping ( soft_name, host_user, card_name, user_name, version_id, begin_date, end_date, rem_01, rem_02, rem_03, ins_time, ins_user, upd_time, upd_user ) SELECT T.soft_name, T.host_user, T.card_name, T.user_name, T.version_id, T.begin_date, T.end_date, T.rem_01, T.rem_02, T.rem_03, T.ins_time, T.ins_user, T.upd_time, T.upd_user FROM #TT T LEFT JOIN user_mapping u ON u.guid_id = T.guid_id WHERE u.guid_id IS NULL OR T.guid_id = '' -- 更新已有记录 UPDATE u SET soft_name = T.soft_name, host_user = T.host_user, card_name = T.card_name, user_name = T.user_name, version_id = T.version_id, begin_date = T.begin_date, end_date = T.end_date, rem_01 = T.rem_01, rem_02 = T.rem_02, rem_03 = T.rem_03, upd_time = T.upd_time, upd_user = T.upd_user FROM #TT T INNER JOIN user_mapping u ON u.guid_id = T.guid_id WHERE T.guid_id <> '' END DROP TABLE #TT; END ``` --- ### ✅ 关键点总结 | 问题 | 解决方法 | |------|----------| | 访问 Deleted 报错 | 使用 `row["guid_id", DataRowVersion.Original]` | | 删除时传入 guid_id 列表 | 使用逗号分隔字符串 `@str_delete` | | SQL 删除 | 使用 `STRING_SPLIT` 解析并删除 | | 避免将删除传入 `#TT` | 删除操作单独处理,不走 `@tb1` 插入更新流程 | --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值