Удаление определенных строк из DataTable - PullRequest
64 голосов
/ 13 апреля 2011

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

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

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

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

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

Ответы [ 13 ]

136 голосов
/ 13 апреля 2011

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

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

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

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

106 голосов
/ 07 декабря 2012

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

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

НО!НО!

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

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


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

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()
15 голосов
/ 24 октября 2012

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

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

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

13 голосов
/ 09 февраля 2012

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

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();
8 голосов
/ 16 июня 2016
DataRow[] dtr=dtPerson.select("name=Joe");
foreach(var drow in dtr)
{
   drow.delete();
}
dtperson.AcceptChanges();

Надеюсь, это поможет вам

4 голосов
/ 20 марта 2013

Или просто преобразовать DataTable Набор строк в список:

foreach(DataRow dr in dtPerson.Rows.ToList())
{
    if(dr["name"].ToString()=="Joe")
    dr.Delete();
}
3 голосов
/ 29 июля 2015

Чтобы удалить всю строку из 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);
1 голос
/ 15 апреля 2017

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

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

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

note : вместо этого ищите 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 не содержит name Joe это вызовет исключение), поэтому Вы ничего не делаете со своим столом, он остается неизменным.

1 голос
/ 16 марта 2016
<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 голос
/ 13 апреля 2011

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

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

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