从一个数据表中删除另一个数据表存在的行。

4
我有一个数据集DataSet中的DataTable dt1,其中包含列C1(PK)、C2和C3(唯一值)。在删除了一些行之后,我将执行AcceptChanges操作。我还有另一个DataTable dt2,其结构与dtA相同。我想删除dtA中存在于dtB中的行,这些行是根据C3(唯一值)进行比较的,并执行AcceptChanges操作。
我可以通过像下面这样使用Except(从here学到的)来实现非常接近但不符合要求:
var a = dt1.AsEnumerable().Select(r => r.Field<Guid>("C3"));
var b = dt2.AsEnumerable().Select(r => r.Field<Guid>("C3"));
var c = a.Except(b);

这种方法的问题在于,我只得到了属于C3列的值列表。我可以像此处所示那样做更多的事情,但是我将会有一个新的DataTable,而不是仍然改变现有的dt1表。
我想到的第二种方法是像下面这样做:
foreach (DataRow r1 in ds1.Tables[0].Rows)
{
    foreach(DataRow r2 in dt2.Rows)
    {
        if (r1.Field<Guid>("Guid1") == r2.Field<Guid>("Guid2"))
        {
            r1.Delete();
        }
    }
}

在这里,一旦行被删除,我就会收到一个错误提示,说“此行已从表中删除,没有任何数据。” 我在这里遗漏了什么?
我也觉得可能有更好的方法来做这个,只是不知道 lol。

您想要删除行还是将其标记为已删除? - Sergey Berezovskiy
我认为那会更好。我没有想到那个。如果我可以将它们标记为已删除,AcceptChange会负责删除工作,不是吗? - haku
是的,AcceptChange会完成这项工作。 - Sergey Berezovskiy
@SergeyBerezovskiy - 我尝试设置RowState,但编译时抛出错误,说它是只读属性。 - haku
1个回答

10
我得到一个错误,提示“此行已从表中删除,没有任何数据”,我错过了什么?
你有两个循环 - 外部和内部。这里有两个问题。第一个是在找到匹配项时删除行。在内部循环的下一次迭代中,您尝试获取已删除行的值,这会引发异常。但即使在删除行后中断内部循环,您仍将遇到另一个问题 - 修改正在枚举的行。因此,您的代码应如下所示:
foreach (DataRow r1 in dt1.Rows.Cast<DataRow>().ToArray()) // save rows to array
{
    foreach (DataRow r2 in dt2.Rows)
    {
        if (r1.Field<Guid>("C3") == r2.Field<Guid>("C3"))
        {
            r1.Delete();
            break; // break inner loop
        }
    }
}

但我会选择使用LINQ方法-通过在id字段上连接行来获取要删除的行,然后从表中删除它们:

var rowsToDelete = from r1 in dt1.AsEnumerable()
                   join r2 in dt2.AsEnumerable()
                        on r1.Field<Guid>("C3") equals r2.Field<Guid>("C3")
                   select r1;

foreach(DataRow row in rowsToDelete.ToArray())
   row.Delete(); // marks row as deleted;

注意:使用 Delete 方法删除现有的 DataRow 后,其 RowState 将变为 Deleted。行将保持 Deleted 状态,直到您调用 AcceptChanges,该方法会将行从表中移除。

我觉得这个应该有效,但是我不知道它是怎么工作的。我原本认为在执行代码片段的第一部分后,rowsToDelete将会有r1的独立副本,而不是指向r1本身。看来我的理解是错误的。 - haku
但是为什么在rowsToDelete中将行标记为已删除,而不是在dt1中标记,这样做会使得在对数据集执行AcceptChanges时可以删除这些行。难道rowsToDelete不是在那段linq代码之后的一个不同的副本吗?我说的有道理吗,哈哈? - haku
@TimSchmelter MSDN 上说,在接受更改后,行将被删除。请参见 Delete() 方法的备注中的第二段。 - Sergey Berezovskiy
@HakuKalay 不是,rowsToDelete不是rows的副本——这是对dt1中已经存在的rows的引用。 - Sergey Berezovskiy
1
@SergeyBerezovskiy - 哦,好的,谢谢。那么如果我只从dt1中选择C1和C3列,rowToDelete是否仍然引用dt1中相应的行? - haku
1
@HakuKalay 不,如果你进行投影操作(即仅选择C1和C3列),那么你将得到一个匿名对象,它与任何DataRow都没有关系。那只是字段的值。 - Sergey Berezovskiy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接