Je veux supprimer certaines lignes de DataTable, mais cela donne une erreur comme celle-ci,
La collection a été modifiée. opération d'énumération peut ne pas s'exécuter
J'utilise pour supprimer ce code,
foreach(DataRow dr in dtPerson.Rows){
if(dr["name"].ToString()=="Joe")
dr.Delete();
}
Alors, quel est le problème et comment le résoudre? Quelle méthode conseillez-vous?
Si vous supprimez un élément d'une collection, celle-ci a été modifiée et vous ne pouvez pas continuer à l'énumérer.
À la place, utilisez une boucle For, telle que:
for(int i = dtPerson.Rows.Count-1; i >= 0; i--)
{
DataRow dr = dtPerson.Rows[i];
if (dr["name"] == "Joe")
dr.Delete();
}
dtPerson.AcceptChanges();
Notez que vous effectuez une itération inverse pour éviter de sauter une ligne après la suppression de l'index actuel.
Avant que tout le monde ne saute sur le mouvement 'vous ne pouvez pas supprimer les lignes d'un énumération}', vous devez d'abord vous rendre compte que les DataTables sont transactionnels, et ne purgent pas techniquement les modifications. jusqu'à ce que vous appeliez AcceptChanges ()
Si vous voyez cette exception alors que vous appelez Delete, vous êtes déjà dans un état de données en attente de modifications. Par exemple, si vous venez de charger à partir de la base de données, l'appel de Delete lève une exception si vous êtes dans une boucle foreach.
MAIS! MAIS!
Si vous chargez des lignes de la base de données et appelez la fonction 'AcceptChanges ()', vous validez toutes les modifications en attente dans le DataTable. Vous pouvez maintenant parcourir la liste des lignes en appelant Delete () sans le moindre souci, car elle marque simplement la ligne pour suppression, mais n'est validée que lorsque vous _ {encore appelez AcceptChanges ()
Je me rends compte que cette réponse est un peu dépassée, mais j'ai récemment dû faire face à un problème similaire et j'espère que cela épargnera un peu la peine pour un futur développeur travaillant sur du code vieux de 10 ans :)
P.s. Voici un exemple de code simple ajouté par Jeff :
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()
avec cette solution:
for(int i = dtPerson.Rows.Count-1; i >= 0; i--)
{
DataRow dr = dtPerson.Rows[i];
if (dr["name"] == "Joe")
dr.Delete();
}
si vous allez utiliser le datatable après avoir supprimé la ligne, vous obtiendrez une erreur. Donc, ce que vous pouvez faire c'est: Remplacer dr.Delete();
par dtPerson.Rows.Remove(dr);
Ça marche pour moi
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();
J'espère que cela vous aidera
Ou alors, convertissez simplement une collection {DataTable} _ Row en liste:
foreach(DataRow dr in dtPerson.Rows.ToList())
{
if(dr["name"].ToString()=="Joe")
dr.Delete();
}
Pour supprimer ligne entière de DataTable , procédez comme suit
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);
Où est le problème: Il est interdit de supprimer des éléments de la collection dans une boucle foreach.
Solution: Faites-le comme Widor l'a écrit ou utilisez deux boucles. Lors du premier passage sur DataTable, vous ne stockez que (dans une liste temporaire) les références aux lignes que vous souhaitez supprimer. Ensuite, dans le deuxième passage sur votre liste temporaire, vous supprimez ces lignes.
Je sais que cette question est très ancienne et que la situation est la même il y a quelques jours.
Le problème était, dans ma table sont env. 10000 lignes, la boucle DataTable
était donc très lente.
Enfin, j'ai trouvé une solution beaucoup plus rapide, où je copie les sources DataTable
avec les résultats souhaités, effacez les sources DataTable
et merge
de temporaires DataTable
dans la source.
note : recherchez plutôt Joe
dans DataRow
appelé name
Vous devez rechercher tous les enregistrements dont le nom n'est pas Joe
(méthode légèrement opposée de recherche)
Il y a exemple (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
J'espère que cette solution plus courte aidera quelqu'un.
Il y a c#
code (je ne sais pas si c'est correct parce que j'ai utilisé le convertisseur en ligne :():
//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;
Bien sûr, j’ai utilisé Try/Catch
au cas où il n’y aurait pas de résultat (par exemple, si votre dtPerson
ne contient pas name
Joe
elle lèvera une exception), vous ne faites rien avec votre table, elle reste inchangée.
<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();
}
Vous essayez ceci pour obtenir et supprimer la colonne id de la table de données
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");
}
Je vois divers éléments de la bonne réponse ici, mais laissez-moi tout résumer et expliquer quelques points.
Tout d'abord, AcceptChanges
ne doit être utilisé que pour marquer la transaction entière d'une table comme étant validée et validée. Ce qui signifie que si vous utilisez DataTable en tant que source de données pour la liaison à un serveur SQL, par exemple, appeler AcceptChanges
manuellement garantira que les modifications ne seront jamais enregistrées sur le serveur SQL .
Ce qui rend cette question plus confuse, c’est qu’il existe en réalité deux cas dans lesquels une exception est levée et que nous devons les prévenir tous les deux.
1. Modification d'une collection IEnumerable
Nous ne pouvons pas ajouter ou supprimer un index à la collection en cours d'énumération, car cela pourrait affecter l'indexation interne de l'énumérateur. Il existe deux façons de contourner ce problème: soit créer votre propre indexation dans une boucle for, soit utiliser une collection distincte (non modifiée) pour l'énumération.
2. Tentative de lecture d'une entrée supprimée
Comme les DataTables sont transactionnel collections, les entrées peuvent être marquées pour suppression mais apparaissent toujours dans l'énumération. Ce qui signifie que si vous demandez une entrée supprimée pour la colonne "name"
, une exception sera levée. Ce qui signifie que nous devons vérifier si dr.RowState != DataRowState.Deleted
avant d'interroger une colonne.
Mettre tous ensemble
Nous pourrions être désordonnés et faire tout cela manuellement, ou nous pourrions laisser le DataTable faire tout le travail pour nous et faire en sorte que l'instruction ressemble davantage à un appel SQL en procédant comme suit:
string name = "Joe";
foreach(DataRow dr in dtPerson.Select($"name='{name}'"))
dr.Delete();
En appelant la fonction Select
de DataTable, notre requête évite automatiquement les entrées déjà supprimées dans DataTable. Et puisque la fonction Select
renvoie un tableau de correspondances, la collection sur laquelle nous énumérons n’est pas modifiée lorsque nous appelons dr.Delete()
. J'ai également épelé l'expression Select avec interpolation de chaîne pour permettre la sélection de variables sans rendre le code bruyant.
J'ai un jeu de données dans mon application et je suis allé définir des modifications (suppression d'une ligne), mais ds.tabales["TableName"]
est en lecture seule. Puis j'ai trouvé cette solution.
C'est une application 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);
}
}