Удаление определенных строк из DataTable



Я хочу удалить некоторые строки из DataTable, но это дает такую ошибку,




коллекция была изменена; операция перечисления может не выполняться




Я использую для удаления этого кода



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


Итак, в чем же проблема и как ее решить? Какой метод вы посоветуете?

997   11  

11 ответов:

Если удалить элемент из коллекции, эта коллекция была изменена и вы не можете продолжать перечислять он.

вместо этого используйте цикл For, например:

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

обратите внимание, что вы повторяете в обратном порядке, чтобы избежать пропуска строки после удаления текущего индекса.

прежде чем все прыгают на 'вы не можете удалить строки в перечислении' bandwagon, вы должны сначала понять, что DataTables являются транзакционные, и технически не очищайте изменения, пока вы не позвоните AcceptChanges ()

Если вы видите это исключение при вызове удалить, вы уже в состоянии ожидания-изменения данных. Например, если вы только что загрузились из базы данных, вызов Delete вызовет исключение, если вы находитесь внутри цикла foreach.

но! Но!

Если вы загружаете строки из базы данных и вызываете функцию' AcceptChanges () ' вы фиксируете все эти ожидающие изменения в DataTable. Теперь вы можете перебирать список строк, вызывающих Delete () без заботы в мире, потому что он просто помечает строку для удаления, но не фиксируется до тех пор, пока вы снова звоните AcceptChanges ()

Я понимаю, что этот ответ немного устарел, но недавно мне пришлось иметь дело с подобной проблемой, и, надеюсь, это сэкономит некоторую боль для будущего разработчика, работающего над 10-летним кодом :)


P. s. Вот простой пример кода, добавленный Джефф:

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()

С этим решением:

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

Если вы собираетесь использовать datatable после удаления строки, вы получите сообщение об ошибке. Так что вы можете сделать, это: заменить dr.Delete(); С dtPerson.Rows.Remove(dr);

это работает для меня,

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();

Я надеюсь, что это поможет вам

или просто преобразовать DataTableколлекция строку список:

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

удалить строку С 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);

где проблема: запрещено удалять элементы из коллекции внутри цикла foreach.

решение: либо сделайте это, как написал Видор, либо используйте две петли. При первом переходе через DataTable вы сохраняете только (во временном списке) ссылки на строки, которые вы хотите удалить. Затем во втором проходе над вашим временным списком вы удаляете эти строки.

<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();
}

я знаю, что это очень старый вопрос, и у меня аналогичная ситуация несколько дней назад.

проблема была в моей таблице приблизительно. 10000 строк, так что пробегаем через DataTable строки были очень медленными.

наконец, я нашел гораздо более быстрое решение, где я делаю копию источника DataTable с желаемыми результатами, очистить источник DataTable и merge результаты временных DataTable в одном источнике.

Примечание : вместо того, чтобы искать Joe на DataRow называется name вы должны искать все записи, у которых нет имени Joe (немного противоположный способ поиска)

есть пример (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

я надеюсь, что это более короткое решение поможет кому-то.

здесь 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;

конечно, я использовал Try/Catch в случае если нет результата (например, если dtPerson не содержит nameJoe это бросит исключение), поэтому вы ничего не делаете с вашей таблицей, она остается неизменной.

у меня есть набор данных в мое приложение, и я пошел, чтобы установить изменения (удаление строки), но ds.tabales["TableName"] только для чтения. Тогда я нашел это решение.

это 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);                 
    }           
}

Comments

    Ничего не найдено.