从DataTable中删除特定行

103

我想从DataTable中删除一些行,但会出现以下错误:

集合已修改; 可能无法执行枚举操作

我使用以下代码进行删除:

foreach(DataRow dr in dtPerson.Rows){
    if(dr["name"].ToString()=="Joe")
        dr.Delete();
}

那么问题是什么,如何解决?你建议使用哪种方法?

14个回答

202

如果您从集合中删除一个项,则该集合已更改,您无法继续枚举它。

相反,使用 For 循环,例如:

for(int i = dtPerson.Rows.Count-1; i >= 0; i--)
{
    DataRow dr = dtPerson.Rows[i];
    if (dr["name"] == "Joe")
        dr.Delete();
}
dtPerson.AcceptChanges();

请注意,您正在反向迭代,以避免在删除当前索引后跳过一行。


@Slugster 比我先了!(不过我把你的 [ii] 改成了 [i],哈哈 :-)) - Widor
11
这是不正确的。你可以使用foreach循环遍历表格并删除行。参见Steve的回答 - Alexander Garden
3
如果我们稍后使用DataTable,它会抛出异常。正确的方法是在源DataTable上调用Remove() - dtPerson.Rows.Remove(dr)。此答案还应包括@bokkie的答案。 - Code.me
@AlexanderGarden,我刚刚尝试使用foreach循环并遇到了以下错误: “System.InvalidOperationException”类型的异常在“System.Data.dll”中发生,但未在用户代码中处理。 额外信息:已修改集合;可能无法执行枚举操作。 我认为你不能再使用foreach循环来解决这个问题了。 - Daryl Bennett
如果要与字符串进行比较,应该使用dr["name"].ToString()=="Joe"。 - raw_hitt
显示剩余4条评论

145
在大家都认为“你不能删除枚举中的行”之前,您需要先意识到DataTable是事务性的,并且直到调用AcceptChanges()才会在技术上清除更改。
如果您在调用Delete时看到此异常,则已处于挂起更改数据状态。例如,如果您刚从数据库加载,如果您在foreach循环内部,则调用Delete将抛出异常。
但是!如果您从数据库加载行并调用函数“AcceptChanges()”,则将提交所有这些待处理更改到DataTable。现在,您可以遍历行列表调用Delete()而不必担心任何问题,因为它只是标记该行以进行删除,但直到再次调用AcceptChanges()时才提交。
我意识到这个回答有点过时了,但我最近也遇到了类似的问题,希望这可以为未来处理10年代码的开发人员节省一些痛苦 :)
P.S. 这里是由Jeff添加的一个简单的代码示例: C#
YourDataTable.AcceptChanges(); 
foreach (DataRow row in YourDataTable.Rows) {
    // If this row is offensive then
    row.Delete();
} 
YourDataTable.AcceptChanges();

VB.Net

ds.Tables(0).AcceptChanges()
For Each row In ds.Tables(0).Rows
    ds.Tables(0).Rows(counter).Delete()
    counter += 1
Next
ds.Tables(0).AcceptChanges()

2
我认为将 object row_loopVariable in ds.Tables(0).Rows 更改为 DataRow row in ds.Tables(0).Rows 会更有帮助。请注意,这是关于编程的内容。 - DotNetDublin
2
这个东西在一个噩梦般的周末部署中救了我。你应该得到所有啤酒! - James Love
好代码。不过,在C#中递增一个计数器的典型方式是 counter++ 而不是 counter+= 1 - MQuiggGeorgia
好的回答,但有时候人们可能不想调用.AcceptChanges()。 - faheem khan
另外要注意,您可以使用row.Delete()或在行集合上调用Remove()来删除一行。为了使此答案起作用,您必须使用Delete(),否则仍会出现错误。 - Mike Cheel
显示剩余3条评论

19

这对我有效,

List<string> lstRemoveColumns = new List<string>() { "ColValue1", "ColVal2", "ColValue3", "ColValue4" };
List<DataRow> rowsToDelete = new List<DataRow>();

foreach (DataRow row in dt.Rows) {
    if (lstRemoveColumns.Contains(row["ColumnName"].ToString())) {
        rowsToDelete.Add(row);
    }
}

foreach (DataRow row in rowsToDelete) {
    dt.Rows.Remove(row);
}

dt.AcceptChanges();

1
很容易忽略掉 dt.AcceptChanges()。 - Matthew Lock
您也可以调用DataRow类的Delete方法来标记要删除的行。调用Remove与调用Delete然后调用AcceptChanges相同。 在遍历DataRowCollection对象时不应在foreach循环中调用Remove。Remove会修改集合的状态。请参阅https://msdn.microsoft.com/de-de/library/system.data.datarowcollection.remove(v=vs.110).aspx谢谢。 - Andreas Krohn

19

通过这个解决方案:

for(int i = dtPerson.Rows.Count-1; i >= 0; i--) 
{ 
    DataRow dr = dtPerson.Rows[i]; 
    if (dr["name"] == "Joe")
        dr.Delete();
} 

如果你在删除行之后仍然要使用datatable,那么就会出现错误。所以你可以这样做: 用dtPerson.Rows.Remove(dr);替换dr.Delete();


10
DataRow[] dtr = dtPerson.Select("name=Joe"); //name is the column in the data table
foreach(var drow in dtr)
{
   drow.Delete();
}
dtperson.AcceptChanges();

1
命令是 drow.Delete(); 而不是 drow.delete(); 在 .net 中方法是区分大小写的。 - MethodMan

7

要从DataTable中删除整行,请按照以下方式操作:

DataTable dt = new DataTable();  //User DataTable
DataRow[] rows;
rows = dt.Select("UserName = 'KarthiK'");  //'UserName' is ColumnName
foreach (DataRow row in rows)
     dt.Rows.Remove(row);

4

或者将 DataTableRow 集合 转换为列表:

foreach(DataRow dr in dtPerson.Rows.ToList())
{
    if(dr["name"].ToString()=="Joe")
    dr.Delete();
}

1
<asp:GridView ID="grd_item_list" runat="server" AutoGenerateColumns="false" Width="100%" CssClass="table table-bordered table-hover" OnRowCommand="grd_item_list_RowCommand">
    <Columns>
        <asp:TemplateField HeaderText="No">
            <ItemTemplate>
                <%# Container.DataItemIndex + 1 %>
            </ItemTemplate>
        </asp:TemplateField>            
        <asp:TemplateField HeaderText="Actions">
            <ItemTemplate>                    
                <asp:Button ID="remove_itemIndex" OnClientClick="if(confirm('Are You Sure to delete?')==true){ return true;} else{ return false;}" runat="server" class="btn btn-primary" Text="REMOVE" CommandName="REMOVE_ITEM" CommandArgument='<%# Container.DataItemIndex+1 %>' />
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>

 **This is the row binding event**

protected void grd_item_list_RowCommand(object sender, GridViewCommandEventArgs e) {

    item_list_bind_structure();

    if (ViewState["item_list"] != null)
        dt = (DataTable)ViewState["item_list"];


    if (e.CommandName == "REMOVE_ITEM") {
        var RowNum = Convert.ToInt32(e.CommandArgument.ToString()) - 1;

        DataRow dr = dt.Rows[RowNum];
        dr.Delete();

    }

    grd_item_list.DataSource = dt;
    grd_item_list.DataBind();
}

1
你可以尝试以下方法来获取并移除数据表中的id列。
if (dt1.Columns.Contains("ID"))
{
    for (int i = dt1.Rows.Count - 1; i >= 0; i--)
    {
        DataRow dr = dt1.Rows[i];

        if (dr["ID"].ToString() != "" && dr["ID"].ToString() != null)
        {
            dr.Delete();
        }
    }

    dt1.Columns.Remove("ID");
}

1
问题所在:在foreach循环内部删除集合中的项是被禁止的。
解决方案:要么像Widor所写的那样做,要么使用两个循环。在第一次遍历DataTable时,您只需将要删除的行的引用(存储在临时列表中)即可。然后在第二次遍历临时列表时,删除这些行。

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