web-dev-qa-db-fra.com

Suppression de lignes spécifiques de DataTable

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? 

59
namco

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.

125
Widor

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

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

13
bokkie

Ç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();
12
Balaji Birajdar
DataRow[] dtr=dtPerson.select("name=Joe");
foreach(var drow in dtr)
{
   drow.delete();
}
dtperson.AcceptChanges();

J'espère que cela vous aidera

8
Karthik

Ou alors, convertissez simplement une collection {DataTable} _ Row en liste:

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

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);
2
Karthikeyan P

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.

1
Al Kepp

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 nameJoe elle lèvera une exception), vous ne faites rien avec votre table, elle reste inchangée.

1
nelek
<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
Arun Prasad E S

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");
}
0
shubham

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.

0
Rhaokiel

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);                 
    }           
}
0
Mamad