Я хочу удалить несколько строк из DataTable, но выдает такую ошибку:
Коллекция была изменена; операция перечисления может не выполняться
Я использую для удаления этот код,
foreach(DataRow dr in dtPerson.Rows){
if(dr["name"].ToString()=="Joe")
dr.Delete();
}
Итак, в чем проблема и как ее исправить? Какой способ посоветуете?
c#
delete-row
намко
источник
источник
[ii]
на[i]
, однако :-)DataTable
последним, это вызовет исключение. Правильный способ - обратитьсяRemove()
к источникуDataTable
-dtPerson.Rows.Remove(dr)
.Delete()
требует вызова, чтобыAcceptChanges()
удаление вступило в силу?Прежде чем все перейдут на подножку `` Вы не можете удалять строки в перечислении '', вам нужно сначала понять, что 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()
источник
object row_loopVariable in ds.Tables(0).Rows
наDataRow row in ds.Tables(0).Rows
counter++
вместоcounter+= 1
.с этим решением:
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();
Я надеюсь это поможет тебе
источник
drow.Delete();
неdrow.delete();
методы чувствительны к регистру в .net кстатиЧтобы удалить всю строку из 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);
источник
Или просто преобразуйте коллекцию DataTable Row в список:
foreach(DataRow dr in dtPerson.Rows.ToList()) { if(dr["name"].ToString()=="Joe") dr.Delete(); }
источник
В чем проблема: запрещено удалять элементы из коллекции внутри цикла 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
не содержит,name
Joe
он выдаст исключение), поэтому вы ничего не делаете со своей таблицей, она остается неизменной.источник
В моем приложении есть набор данных, и я решил внести в него изменения (удалить строку), но
ds.tabales["TableName"]
он доступен только для чтения. Тогда я нашел это решение.Это
C#
приложение WPF ,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); } }
источник
Вы пробуете это для получения и удаления столбца идентификатора из таблицы данных
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"); }
источник
Я вижу здесь разные фрагменты правильного ответа, но позвольте мне собрать все воедино и объяснить пару вещей.
Прежде всего, его
AcceptChanges
следует использовать только для того, чтобы пометить всю транзакцию в таблице как подтвержденную и зафиксированную. Это означает, что если вы используете DataTable в качестве источника данных для привязки, например, к серверу SQL, то вызовAcceptChanges
вручную гарантирует, что изменения никогда не будут сохранены на сервере SQL .Что делает эту проблему более запутанной, так это то, что на самом деле существует два случая, в которых возникает исключение, и мы должны предотвратить их оба.
1. Изменение коллекции IEnumerable
Мы не можем добавить или удалить индекс для перечисляемой коллекции, потому что это может повлиять на внутреннюю индексацию перечислителя. Есть два способа обойти это: либо выполнить собственную индексацию в цикле for, либо использовать отдельную коллекцию (которая не изменяется) для перечисления.
2. Попытка прочитать удаленную запись
Поскольку DataTables являются коллекциями транзакций , записи могут быть помечены для удаления, но все равно будут отображаться в перечислении. Это означает, что если вы запросите удаленную запись для столбца,
"name"
она выдаст исключение. Это означает, чтоdr.RowState != DataRowState.Deleted
перед запросом столбца мы должны проверить, есть ли .Собираем все вместе
Мы можем запутаться и сделать все это вручную, или мы можем позволить DataTable сделать всю работу за нас и заставить оператор выглядеть и больше походить на вызов SQL, выполнив следующие действия:
string name = "Joe"; foreach(DataRow dr in dtPerson.Select($"name='{name}'")) dr.Delete();
Вызывая
Select
функцию DataTable , наш запрос автоматически избегает уже удаленных записей в DataTable. А посколькуSelect
функция возвращает массив совпадений, перечисляемая нами коллекция не изменяется при вызовеdr.Delete()
. Я также приправил выражение Select интерполяцией строк, чтобы можно было выбирать переменные, не делая код шумным.источник
простой способ использовать это в кнопке:
var table = $('#example1').DataTable(); table.row($(`#yesmediasec-${id}`).closest('tr')).remove( ).draw();
example1 = таблица идентификаторов. yesmediasec = идентификатор кнопки в строке
используйте это и все будет в порядке
источник