简体   繁体   English

从数据表中删除特定行

[英]Deleting specific rows from DataTable

I want to delete some rows from DataTable, but it gives an error like this,我想从数据表中删除一些行,但它给出了这样的错误,

Collection was modified;集合被修改; enumeration operation might not execute枚举操作可能不会执行

I use for deleting this code,我用于删除此代码,

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

So, what is the problem and how to fix it?那么,问题是什么以及如何解决? Which method do you advise?你建议哪种方法?

If you delete an item from a collection, that collection has been changed and you can't continue to enumerate through it.如果您从集合中删除了一个项目,则该集合已被更改并且您无法继续枚举它。

Instead, use a For loop, such as:相反,使用 For 循环,例如:

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

Note that you are iterating in reverse to avoid skipping a row after deleting the current index.请注意,您正在反向迭代以避免在删除当前索引后跳过一行。

Before everyone jumps on the ' You can't delete rows in an Enumeration ' bandwagon, you need to first realize that DataTables are transactional , and do not technically purge changes until you call AcceptChanges()在每个人都跳上“您不能删除枚举中”的潮流之前,您需要首先意识到 DataTables 是事务性的,并且在调用AcceptChanges()之前不要从技术上清除更改

If you are seeing this exception while calling Delete , you are already in a pending-changes data state .如果您在调用Delete 时看到此异常,则您已经处于待定更改数据状态 For instance, if you have just loaded from the database, calling Delete would throw an exception if you were inside a foreach loop.例如,如果您刚刚从数据库加载,并且在 foreach 循环中调用 Delete 将引发异常。

BUT!但! BUT!但!

If you load rows from the database and call the function ' AcceptChanges() ' you commit all of those pending changes to the DataTable.如果您从数据库加载行并调用函数“ AcceptChanges() ”,则您将所有这些挂起的更改提交到 DataTable。 Now you can iterate through the list of rows calling Delete() without a care in the world, because it simply ear-marks the row for Deletion, but is not committed until you again call AcceptChanges()现在您可以遍历调用 Delete() 的行列表而无需关心,因为它只是将行标记为删除,但在您再次调用AcceptChanges()之前不会提交

I realize this response is a bit dated, but I had to deal with a similar issue recently and hopefully this saves some pain for a future developer working on 10-year-old code :)我意识到这个回复有点过时了,但我最近不得不处理一个类似的问题,希望这可以为未来使用 10 年旧代码的开发人员节省一些痛苦:)


Ps Here is a simple code example added by Jeff : Ps 这是Jeff添加的简单代码示例:

C# C#

YourDataTable.AcceptChanges(); 
foreach (DataRow row in YourDataTable.Rows) {
    // If this row is offensive then
    row.Delete();
} 
YourDataTable.AcceptChanges();

VB.Net 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()

with this solution:使用此解决方案:

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

if you are going to use the datatable after deleting the row, you will get an error.如果要在删除行后使用数据表,则会出现错误。 So what you can do is: replace dr.Delete();所以你可以做的是:替换dr.Delete(); with dtPerson.Rows.Remove(dr);dtPerson.Rows.Remove(dr);

This works for me,这对我有用,

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();
DataRow[] dtr=dtPerson.select("name=Joe");
foreach(var drow in dtr)
{
   drow.delete();
}
dtperson.AcceptChanges();

I hope it will help you我希望它会帮助你

To remove entire row from DataTable , do like this要从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);

Or just convert a DataTable Row collection to a list:或者只是将DataTable Row 集合转换为列表:

foreach(DataRow dr in dtPerson.Rows.ToList())
{
    if(dr["name"].ToString()=="Joe")
    dr.Delete();
}
<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();
}

I know this is, very, old question, and I have similar situation few days ago.我知道这是一个非常古老的问题,几天前我也遇到过类似的情况。

Problem was, in my table are approx.问题是,在我的桌子上大约是。 10000 rows, so looping trough DataTable rows was very slow. 10000 行,因此循环遍历DataTable行非常慢。

Finally, I found much faster solution, where I make copy of source DataTable with desired results, clear source DataTable and merge results from temporary DataTable into source one.最后,我找到了更快的解决方案,在该解决方案中,我复制了具有所需结果的源DataTable ,清除源DataTable并将临时DataTable结果merge到源DataTable

note : instead search for Joe in DataRow called name You have to search for all records whose not have name Joe (little opposite way of searching)注意:而是在名为name DataRow搜索Joe您必须搜索所有没有姓名Joe记录(有点相反的搜索方式)

There is example ( vb.net ) :有例子( vb.net ):

'Copy all rows into tmpTable whose not contain Joe in name DataRow
Dim tmpTable As DataTable = drPerson.Select("name<>'Joe'").CopyToTable
'Clear source DataTable, in Your case dtPerson
dtPerson.Clear()
'merge tmpTable into dtPerson (rows whose name not contain Joe)
dtPerson.Merge(tmpTable)
tmpTable = Nothing

I hope so this shorter solution will help someone.我希望这个更短的解决方案会对某人有所帮助。

There is c# code (not sure is it correct because I used online converter :( ):c#代码(不确定它是否正确,因为我使用了在线转换器 :():

//Copy all rows into tmpTable whose not contain Joe in name DataRow
DataTable tmpTable = drPerson.Select("name<>'Joe'").CopyToTable;
//Clear source DataTable, in Your case dtPerson
dtPerson.Clear();
//merge tmpTable into dtPerson (rows whose name not contain Joe)
dtPerson.Merge(tmpTable);
tmpTable = null;

Of course, I used Try/Catch in case if there is no result (for example, if Your dtPerson don't contain name Joe it will throw exception), so You do nothing with Your table, it stays unchanged.当然,如果没有结果,我使用了Try/Catch (例如,如果您的dtPerson不包含name Joe ,它将抛出异常),因此您对您的表不做任何操作,它保持不变。

Where is the problem: It is forbidden to delete items from collection inside a foreach loop.问题出在哪里:禁止在 foreach 循环内从集合中删除项目。

Solution: Either do it like Widor wrote, or use two loops.解决方案:要么像 Widor 写的那样做,要么使用两个循环。 In the first pass over DataTable you only store (in a temporary list) the references to rows you want to delete.在第一次通过 DataTable 时,您只存储(在临时列表中)对要删除的行的引用。 Then in the second pass over your temporary list you delete those rows.然后在第二次遍历临时列表时删除这些行。

I have a dataset in my app and I went to set changes (deleting a row) to it but ds.tabales["TableName"] is read only.我的应用程序中有一个数据集,我去设置更改(删除一行),但ds.tabales["TableName"]是只读的。 Then I found this solution.然后我找到了这个解决方案。

It's a wpf C# app,这是一个 wpf C#应用程序,

try {
    var results = from row in ds.Tables["TableName"].AsEnumerable() where row.Field<string>("Personalid") == "47" select row;                
    foreach (DataRow row in results) {
        ds.Tables["TableName"].Rows.Remove(row);                 
    }           
}

You try this for getting and removing id column from data table您尝试使用此方法从数据表中获取和删除 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");
}

I'm seeing various bits and pieces of the right answer here, but let me bring it all together and explain a couple of things.我在这里看到了正确答案的各种零碎部分,但让我把它们放在一起并解释一些事情。

First of all, AcceptChanges should only be used to mark the entire transaction on a table as being validated and committed.首先, AcceptChanges应该只用于将表上的整个事务标记为已验证和提交。 Which means if you are using the DataTable as a DataSource for binding to, for example, an SQL server, then calling AcceptChanges manually will guarantee that that the changes never get saved to the SQL server .这意味着如果您使用 DataTable 作为绑定到 SQL 服务器的数据源,则手动调用AcceptChanges将保证更改永远不会保存到 SQL 服务器

What makes this issue more confusing is that there are actually two cases in which the exception is thrown and we have to prevent both of them.让这个问题更令人困惑的是,实际上有两种情况会抛出异常,我们必须防止这两种情况。

1. Modifying an IEnumerable's Collection 1.修改一个IEnumerable的集合

We can't add or remove an index to the collection being enumerated because doing so may affect the enumerator's internal indexing.我们不能向被枚举的集合添加或删除索引,因为这样做可能会影响枚举器的内部索引。 There are two ways to get around this: either do your own indexing in a for loop, or use a separate collection (that is not modified) for the enumeration.有两种方法可以解决这个问题:要么在 for 循环中进行自己的索引,要么使用单独的集合(未修改)进行枚举。

2. Attempting to Read a Deleted Entry 2. 尝试阅读已删除的条目

Since DataTables are transactional collections, entries can be marked for deletion but still appear in the enumeration.由于数据表是事务性集合,条目可以被标记为删除,但仍会出现在枚举中。 Which means that if you ask a deleted entry for the column "name" then it will throw an exception.这意味着如果您向已删除的条目询问列"name"那么它将引发异常。 Which means we must check to see whether dr.RowState != DataRowState.Deleted before querying a column.这意味着我们必须在查询列之前检查是否dr.RowState != DataRowState.Deleted

Putting it all together把这一切放在一起

We could get messy and do all of that manually, or we can let the DataTable do all the work for us and make the statement look and at more like an SQL call by doing the following:我们可能会变得混乱并手动完成所有这些工作,或者我们可以让 DataTable 为我们完成所有工作,并通过执行以下操作使语句看起来更像 SQL 调用:

string name = "Joe";
foreach(DataRow dr in dtPerson.Select($"name='{name}'"))
    dr.Delete();

By calling DataTable's Select function, our query automatically avoids already deleted entries in the DataTable.通过调用 DataTable 的Select函数,我们的查询会自动避免 DataTable 中已删除的条目。 And since the Select function returns an array of matches, the collection we are enumerating over is not modified when we call dr.Delete() .由于Select函数返回一个匹配数组,因此在调用dr.Delete()时不会修改我们正在枚举的集合。 I've also spiced up the Select expression with string interpolation to allow for variable selection without making the code noisy.我还为 Select 表达式添加了字符串插值,以允许在不使代码嘈杂的情况下进行变量选择。

the easy way use this in button :在按钮中使用它的简单方法:

 var table = $('#example1').DataTable();
 table.row($(`#yesmediasec-${id}`).closest('tr')).remove( ).draw();

example1 = id table .示例 1 = id 表。 yesmediasec = id of the button in the row yesmediasec = 行中按钮的 id

use it and every thing will be ok使用它,一切都会好的

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM