数据表格,如何有条件地删除行。

46

我正在学习C#,进展不错。但是,我现在遇到了第一个"不理解"的时刻。

DataTable提供对其Rows集合的随机行访问,不仅可以通过典型的集合行为,还可以通过DataTable.Select访问。然而,我似乎无法将此功能与DataRow.Delete联系起来。目前,这似乎是我需要执行的条件删除一个或多个表中的行的操作。

int max = someDataTable.Rows.Count - 1;
for(int i = max; i >= 0; --i)
{
    if((int)someDataTable.Rows[i].ItemArray[0] == someValue)
    {
        someDataTable.Rows[i].BeginEdit();
        someDataTable.Rows[i].Delete();
    }
    else
        break;
}
someDataTable.AcceptChanges();

但是我对这段代码并不满意。我也不太确定。我一定漏掉了什么。如果我需要有条件地删除一个或多个行,那我真的被迫按顺序遍历Rows集合吗?

(不要在意倒序的for循环。我正在从数据表的末尾开始删除。所以没问题)

4个回答

85

你可以查询数据集,然后循环选择的行将它们设置为删除。

var rows = dt.Select("col1 > 5");
foreach (var row in rows)
   { row.Delete(); }
   dt.AcceptChanges();
   

...你还可以创建一些扩展方法使它更加容易...

myTable.Delete("col1 > 5");

public static DataTable Delete(this DataTable table, string filter)
{
    table.Select(filter).Delete();
    return table;
}
public static void Delete(this IEnumerable<DataRow> rows)
{
    foreach (var row in rows)
        row.Delete();
}

啊!我没想到Select()会返回对数据表行的引用。我知道我一定漏掉了什么。非常感谢! - Alexandre Bell
太好了,这也解决了我的datatable.select更改顺序的问题。 - Signcodeindie
6
最后,您想使用以下命令将这些更改应用于dt表格。dt.AcceptChanges() - Damith
这是一种有用的方法,但是,如果您需要使用变量而不是简单数字来检查一列(行值),请小心,解释器将尝试查找另一列与变量名相同的列。例如:' var rows = Dt.Select("Col1 < yourVariable); ',代码将尝试查找其中[Column.Col1]小于[Column.yourVariable]的行,这将导致错误。 - Lorenzo Bassetti

48

使用 LINQ 可以编写一行代码,并避免运行时评估任何选择字符串:

someDataTable.Rows.Cast<DataRow>().Where(
    r => r.ItemArray[0] == someValue).ToList().ForEach(r => r.Delete());

2
我真的很喜欢这个解决方案-非常好-谢谢! - MDV2000
3
删除操作后,有时需要记得调用 someDataTable.AcceptChanges() 函数。 - Wolf
1
@GreyWolf 如果你想将更改写回数据库,请不要调用AcceptChanges! AcceptChanges会将所有新/更新的行标记为未更改,因此DataAdapter会认为没有任何更改,并且在.Update()上不会将任何内容写入数据库。 - The Conspiracy
1
在这种情况下,r.ItemArray [0]是什么?它是行的第一列吗?回复时请标记我。 - user3281466
这是非常漂亮和优雅的代码。我已经寻找这个技巧两天了!非常感谢@Alain发布这段代码,因为你救了我的一天。 - Rohan Rao

11

我手头没有Windows电脑来尝试这个,但我认为你可以使用DataView并进行如下操作:

DataView view = new DataView(ds.Tables["MyTable"]);
view.RowFilter = "MyValue = 42"; // MyValue here is a column name

// Delete these rows.
foreach (DataRowView row in view)
{
  row.Delete();
}

虽然我没有测试过这个,但你可以试一试。


我尝试了一下,它起作用了。虽然我更喜欢选择的方法,但这是一个值得记住的好方法。谢谢 :) +1 - Alexandre Bell
这样做会保留底层的 DataTable 对象不变,并且会有未使用的行“增长”,因此可能会成为内存垃圾。如果您想在不同的时刻或不同的对象中检索 DataTable 的不同“视图”,那么这确实是有用的。有趣的方法!谢谢。 - Lorenzo Bassetti

1
基于Linq的扩展方法
public static void DeleteRows(this DataTable dt, Func<DataRow, bool> predicate)
{
    foreach (var row in dt.Rows.Cast<DataRow>().Where(predicate).ToList())
        row.Delete();
}

然后使用:
DataTable dt = GetSomeData();
dt.DeleteRows(r => r.Field<double>("Amount") > 123.12 && r.Field<string>("ABC") == "XYZ");

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