Chcę usunąć niektóre wiersze z DataTable, ale powoduje to taki błąd,
Kolekcja została zmodyfikowana; operacja wyliczenia może nie zostać wykonana
Używam do usunięcia tego kodu,
foreach(DataRow dr in dtPerson.Rows){
if(dr["name"].ToString()=="Joe")
dr.Delete();
}
Na czym więc polega problem i jak go naprawić? Którą metodę radzisz?
c#
delete-row
namco
źródło
źródło
[ii]
TO[i]
jednak :-)DataTable
późniejszego, wyrzuci wyjątek. Prawidłowym sposobem byłoby odwołanieRemove()
się do źródłaDataTable
-dtPerson.Rows.Remove(dr)
.Delete()
wymaga wywołania, abyAcceptChanges()
usunięcie odniosło skutek?Zanim wszyscy przejdą na modę „ Nie możesz usunąć wierszy w wyliczeniu ”, musisz najpierw zdać sobie sprawę, że DataTables są transakcyjne i nie czyść zmian technicznie, dopóki nie wywołasz AcceptChanges ()
Jeśli widzisz ten wyjątek podczas wywoływania funkcji Delete , oznacza to, że jesteś już w stanie danych oczekujących na zmiany . Na przykład, jeśli właśnie załadowałeś z bazy danych, wywołanie Delete zgłosi wyjątek, jeśli jesteś w pętli foreach.
ALE! ALE!
Jeśli załadujesz wiersze z bazy danych i wywołasz funkcję „ AcceptChanges () ”, zatwierdzasz wszystkie te oczekujące zmiany w DataTable. Teraz możesz iterować listę wierszy wywołując Delete () bez troski w świecie, ponieważ po prostu zaznacza wiersz do usunięcia, ale nie jest zatwierdzany, dopóki nie wywołasz ponownie AcceptChanges ()
Zdaję sobie sprawę, że ta odpowiedź jest nieco przestarzała, ale ostatnio miałem do czynienia z podobnym problemem i mam nadzieję, że zaoszczędzi to trochę bólu przyszłemu programistowi pracującemu nad 10-letnim kodem :)
Ps Oto prosty przykład kodu dodany przez Jeffa :
DO#
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()
źródło
object row_loopVariable in ds.Tables(0).Rows
doDataRow row in ds.Tables(0).Rows
counter++
zamiastcounter+= 1
.z tym rozwiązaniem:
for(int i = dtPerson.Rows.Count-1; i >= 0; i--) { DataRow dr = dtPerson.Rows[i]; if (dr["name"] == "Joe") dr.Delete(); }
jeśli zamierzasz używać datatable po usunięciu wiersza, pojawi się błąd. Więc co można zrobić, to zamienić
dr.Delete();
zdtPerson.Rows.Remove(dr);
źródło
To działa dla mnie,
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();
źródło
DataRow[] dtr=dtPerson.select("name=Joe"); foreach(var drow in dtr) { drow.delete(); } dtperson.AcceptChanges();
Mam nadzieję, że ci to pomoże
źródło
drow.Delete();
niedrow.delete();
metody rozróżniaAby usunąć cały wiersz z DataTable , wykonaj następujące czynności
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);
źródło
Lub po prostu przekonwertuj kolekcję DataTable Row na listę:
foreach(DataRow dr in dtPerson.Rows.ToList()) { if(dr["name"].ToString()=="Joe") dr.Delete(); }
źródło
Gdzie jest problem: Zabrania się usuwania elementów z kolekcji wewnątrz pętli foreach.
Rozwiązanie: Albo zrób to tak, jak napisał Widor, albo użyj dwóch pętli. W pierwszym przejściu przez DataTable przechowujesz (na liście tymczasowej) tylko odwołania do wierszy, które chcesz usunąć. Następnie w drugim przejściu nad tymczasową listą usuwasz te wiersze.
źródło
<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(); }
źródło
Wiem, że to bardzo stare pytanie i mam podobną sytuację kilka dni temu.
Problem w tym, że w moim stoliku są ok. 10000 rzędów, więc zapętlanie
DataTable
rzędów koryta było bardzo powolne.W końcu znalazłem znacznie szybsze rozwiązanie, w którym wykonuję kopię źródła
DataTable
z pożądanymi wynikami, czystym źródłemDataTable
imerge
wynikami z tymczasowegoDataTable
na źródłowe.uwaga : zamiast szukać
Joe
wDataRow
nazwanym,name
musisz wyszukać wszystkie rekordy, które nie mają nazwyJoe
(trochę odwrotny sposób wyszukiwania)Oto przykład (
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
Mam nadzieję, że to krótsze rozwiązanie komuś pomoże.
Jest
c#
kod (nie jestem pewien, czy jest poprawny, ponieważ użyłem konwertera online :()://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;
Oczywiście użyłem
Try/Catch
w przypadku, gdy nie ma wyniku (na przykład, jeśli TwójdtPerson
nie zawieraname
Joe
, wyrzuci wyjątek), więc nic nie robisz ze swoim stołem, pozostaje niezmieniony.źródło
Mam zestaw danych w mojej aplikacji i poszedłem ustawić zmiany (usunięcie wiersza), ale
ds.tabales["TableName"]
jest tylko do odczytu. Wtedy znalazłem to rozwiązanie.To
C#
aplikacja 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); } }
źródło
Próbujesz tego w celu pobrania i usunięcia kolumny id z tabeli danych
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"); }
źródło
Widzę tutaj różne fragmenty właściwej odpowiedzi, ale pozwólcie, że połączę to wszystko razem i wyjaśnię kilka rzeczy.
Przede wszystkim
AcceptChanges
powinno być używane tylko do oznaczania całej transakcji w tabeli jako sprawdzonej i zatwierdzonej. Oznacza to, że jeśli używasz DataTable jako źródła danych do tworzenia powiązań, na przykład z serwerem SQL,AcceptChanges
ręczne wywołanie zagwarantuje, że zmiany nigdy nie zostaną zapisane na serwerze SQL .To, co sprawia, że ta kwestia jest bardziej zagmatwana, to fakt, że w rzeczywistości istnieją dwa przypadki, w których wyjątek jest zgłaszany i musimy im zapobiegać.
1. Modyfikowanie kolekcji IEnumerable
Nie możemy dodać ani usunąć indeksu do wyliczanej kolekcji, ponieważ może to wpłynąć na wewnętrzne indeksowanie modułu wyliczającego. Istnieją dwa sposoby obejścia tego problemu: albo wykonaj własne indeksowanie w pętli for, albo użyj oddzielnej kolekcji (która nie jest modyfikowana) do wyliczenia.
2. Próba odczytania usuniętego wpisu
Ponieważ DataTables są kolekcjami transakcyjnymi , wpisy można oznaczać do usunięcia, ale nadal pojawiają się w wyliczeniu. Co oznacza, że jeśli poprosisz o usunięty wpis w kolumnie
"name"
, zgłosi wyjątek. Co oznacza, że musimy sprawdzić, czydr.RowState != DataRowState.Deleted
przed zapytaniem o kolumnę.Kładąc wszystko razem
Moglibyśmy zrobić bałagan i zrobić to wszystko ręcznie, lub możemy pozwolić DataTable wykonać całą pracę za nas i sprawić, by instrukcja wyglądała bardziej jak wywołanie SQL, wykonując następujące czynności:
string name = "Joe"; foreach(DataRow dr in dtPerson.Select($"name='{name}'")) dr.Delete();
Wywołując
Select
funkcję DataTable , nasze zapytanie automatycznie unika już usuniętych wpisów w DataTable. A ponieważSelect
funkcja zwraca tablicę dopasowań, wyliczana przez nas kolekcja nie jest modyfikowana podczas wywołaniadr.Delete()
. Doprawiłem również wyrażenie Select za pomocą interpolacji ciągów, aby umożliwić wybór zmiennych bez hałaśliwego kodu.źródło
w łatwy sposób użyj tego w przycisku:
var table = $('#example1').DataTable(); table.row($(`#yesmediasec-${id}`).closest('tr')).remove( ).draw();
example1 = tabela id. yesmediasec = identyfikator przycisku w wierszu
użyj go i wszystko będzie dobrze
źródło