41

I don't understand why this code does not work.

foreach (DataRow dataRow in dataTable.Rows)
{
    if (true)
    {
        dataRow.Delete();
    }
}
Adam Eberbach
  • 12,309
  • 6
  • 62
  • 114
Jack Kada
  • 24,474
  • 29
  • 82
  • 106

15 Answers15

61

Safest way - use for loop

for (int i = datatable.Rows.Count - 1; i >= 0; i--) 
{
    if (true)
    {
        datatable.Rows[i].Delete();
    }
}

Don't forget to AcceptChanges to remove all marked rows:

datatable.AcceptChanges();
VMAtm
  • 27,943
  • 17
  • 79
  • 125
  • 1
    Nice. Use 'datatable.AcceptChanges()' to really delete the rows afterwards. Otherwise, they are still in the datatable - with a RowState of 'deleted' – peter May 06 '15 at 11:36
  • why the "if(true)" , is that necessary ? – Manan Shah Jul 13 '17 at 13:05
  • 2
    `if (true)` is a placeholder to check, do you need to delete the row. Otherwise all your rows from a datatable will be removed – VMAtm Jul 13 '17 at 14:17
36

Even though DataRow.Delete doesn't modify the state of the collection, Microsoft documentation states that you shouldn't call it while iterating over the collection:

Neither Delete nor Remove should be called in a foreach loop while iterating through a DataRowCollection object. Delete nor Remove modify the state of the collection.

The best solution is usually to create a separate collection (e.g. a List<DataRow>) of items you want to remove, and then remove them after you've finished iterating.

This is also the solution for situations where you want to remove items from a collection, as most collections in .NET don't allow you to change the contents of the collection while you're iterating over it.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • dataRow.Delete does not remove the row from the collection and does not cause the collections size to change nor does it cause the collection to be reordered so a foreach(DataRow row in table.Rows) { row.Delete(); } as long as acceptChanges is not called on an object that would cause the row collection to be changed in length. – Moses May 12 '19 at 06:00
  • @Moses: Hmm... I don't have time to investigate this now, unfortunately... [The documentation](https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/dataset-datatable-dataview/datarow-deletion) does state: "Neither Delete nor Remove should be called in a foreach loop while iterating through a DataRowCollection object. Delete nor Remove modify the state of the collection." I'll edit that in. – Jon Skeet May 12 '19 at 07:25
  • not a native speaker, still that second **nor** does not seem to make sense. what is that supposed to mean? – Cee McSharpface May 23 '21 at 16:19
  • 2
    @CeeMcSharpface: It means "Delete doesn't modify the state of the collection, and Remove doesn't modify the state of the collection." – Jon Skeet May 23 '21 at 17:08
  • thanks. got it. so the recommendation is to not do it because invoking methods on foreach loop variables that look like they could modify the enumerable is a *bad pattern in general*, and not because it would be harmful *in this particular scenario*. – Cee McSharpface May 23 '21 at 17:45
  • @CeeMcSharpface: To be honest, I can't remember the details 11 years later... – Jon Skeet May 23 '21 at 18:05
  • understandable. still relevant though, I had exactly this in a (professional, business) code review today, criticized it and wanted to include an informed "why" - which I could, thanks to this answer and clarifications. – Cee McSharpface May 23 '21 at 19:58
  • @Moses I used to do this on NET 4.5. Upgrading to NET 4.8 causes me headache, which is apparently because I do this (iterating through foreach, performing DataRow.Delete(), calling DataTable.AcceptChanges() at the end). I think this method used to work in the past, but now we really should change it. – Daniel Wu May 28 '21 at 08:53
21

You cannot modify a collection while you're iterating on it using a foreach statement.

you can try something like that :

List<DataRow> deletedRows = new List<DataRow>();

foreach (DataRow dataRow in dataTable.Rows)
{
    if(true) deletedRows.Add(dataRow);
}

foreach(DataRow dataRow in deletedRows)
{
    dataRow.Delete();
}
Thibault Falise
  • 5,795
  • 2
  • 29
  • 32
  • 7
    Incorrect. You cannot modify a collection while iterating through it with a "foreach" You can use a standard 'for' loop. eg: `for(int i = datatable.Rows.length - 1; i >= 0; i --) { //remove rows }` – AllenG Apr 19 '10 at 16:20
  • Deletion is possible while iteration is on http://stackoverflow.com/questions/3150216/data-table-delete-a-row-in-c-using-loop – Developer Jun 30 '10 at 14:49
  • @fzshah76 : No, in your example, using the `Select()` method creates a new collection containing all rows of the previous collection. You are not iterating on the original collection, which contains the row you are deleting. – Thibault Falise Jun 30 '10 at 16:08
  • Nice solution, not overly hackish, works great. But since jumping through this extra little hoop is so easy it makes me wonder why M$ doesn't just make this natively possible within the iteration, even if under the hood they're jumping through the hoop for us. – HerrimanCoder Jul 04 '15 at 18:10
9

If you call the delete method you just have to call AcceptChanges() on the table you are modifying, after the foreach loop.

foreach (DataRow dataRow in dataTable.Rows)
{
    if (true)
    {
        dataRow.Delete();
    }
}

dataTable.AcceptChanges();

The delete method simply marks the row for deletion.

http://msdn.microsoft.com/en-us/library/system.data.datarow.delete%28v=VS.90%29.aspx

Marvin Pinto
  • 30,138
  • 7
  • 37
  • 54
Chris
  • 99
  • 1
  • 1
5

may be my answer not longer useful. exception throw in Foreach with DataRow only appear in .Net 2.0 and earlier, the reason is description at msdn http://msdn.microsoft.com/en-us/library/system.data.datarow.delete(v=vs.80).aspx

If the RowState of the row is Added, the row is removed from the table.

The RowState becomes Deleted after you use the Delete method. It remains Deleted until you call AcceptChanges.

A deleted row can be undeleted by invoking RejectChanges.

to pass this problem you can call DataTable.AcceptChanges() before using foreach

neverlandx
  • 51
  • 1
  • 1
3
foreach (DataRow dataRow in dataTable.Rows)
{
    if (true)
    {
        dataRow.Delete();
    }
}

dataTable.AcceptChanges();

Please Refer the snaps to understatnd the working of it.

  1. Just Deleted but not removed from the DataTable.

enter image description here

  1. Break point before AcceptChanges() Function. enter image description here
  2. After Eexecuting AcceptChanges() Function. enter image description here

I Hope this issue resolved now.

Rahul Uttarkar
  • 3,367
  • 3
  • 35
  • 40
  • 2
    I agree with your answer, however how do you explain this: http://msdn.microsoft.com/en-us/library/system.data.datarow.delete.aspx "Delete should not be called in a foreach loop while iterating through a DataRowCollection object. Delete modifies the state of the collection." Error in MSDN documentation? – Eternal21 Oct 12 '14 at 19:38
3

This is because it's something like try to disassemble the stair that you climb. Simply, you cannot remove an item you iterate.

Therefore you should use different array to iterate and remove them from datatable Rows property.

// Select() method returns an array and you can iterate through while remove
foreach (DataRow row in dataTable.Select())
{
    if (true)
    {
        dataTable.Rows.Remove(row);
    }
}
iBener
  • 469
  • 6
  • 18
2

The Rows content changes while you are iterating if you delete one row, which renders the iteration invalid.

You can, however, copy the rows into a collection first and then iterate over the collection and delete the rows that way. This makes sure that the iteration is not interrupted by changing data to be iterated.

Lucero
  • 59,176
  • 9
  • 122
  • 152
2

The easiest way to achieve this by using a List to map rows you want to delete and then delete rows outside DataTable iteration.

C#

    List<DataRow> rowsWantToDelete= new List<DataRow>();

    foreach (DataRow dr in dt.Rows)
    {
        if(/*Your condition*/)
        {
            rowsWantToDelete.Add(dr);
        }
    }

    foreach(DataRow dr in rowsWantToDelete)
    {
        dt.Rows.Remove(dr);
    }

VB

Dim rowsWantToDelete As New List(Of DataRow)

For Each dr As DataRow In dt
    If 'Your condition' Then
        rowsWantToDelete .Add(dr)
    End If
Next

For Each dr As DataRow In rowsWantToDelete 
    dt.Rows.Remove(dr)
Next
Nishantha
  • 6,065
  • 6
  • 33
  • 51
2

There is an other version of it (I think an easier) what I just used:

int i=0;
while (i < myDataTable.Rows.Count)
{
    if (condition)  //should it be deleted?
        myDataTable.Rows.RemoveAt(i);
    else
        i++;
}

This faster.

Jettero
  • 133
  • 1
  • 8
2

Only for people who are looking for specific scenario like me, I needed to shorten time taken, and once some useful infomation is extracted from each row, I excluded the row by marking as deleted.

Hope this help someone...

foreach (DataRow dataRow in dataTable.Rows)
{
    if (dataRow.RowState != DataRowState.Deleted)
    {
        if (your condition here)
        {
            dataRow.Delete();
        }
    }
}
Kay Lee
  • 922
  • 1
  • 12
  • 40
0

This applies to pretty much any collection. If you try to delete an item while looping through the collection, you will have a problem. For example, if you delete row #3, then the previous row #4 becomes row #3.

DOK
  • 32,337
  • 7
  • 60
  • 92
0

Use this:

for (int i = 0; i < myDataTable.Rows.Count; i++)

{

 myDataTable[i].Delete();

}
Gonzalo.-
  • 12,512
  • 5
  • 50
  • 82
  • 3
    You actually want to do this the other way `(int i - mydatatable.rows.count -1; i >= 0; i--)` Your way can lead to rows being skipped (and, in my experience, usually does. – AllenG Apr 19 '10 at 16:21
0

Sure Magents

This is how I did it and works fine

dt = GetStationeryConsolidationDetails(txtRefNo.Text);
int intRows = dt.Rows.Count;
int x = 0;
for (int c = 0; c < intRows; c++)
{
  if (dt.Rows[c - x]["DQTY"].ToString() == "0")
  {
    dt.Rows[c - x].Delete();
    dt.AcceptChanges();
    x++;        
  }
}
cosmoonot
  • 2,161
  • 3
  • 32
  • 38
Sam
  • 1
  • 3
    **From review queue**: May I request you to please add some more context around your answer. Code-only answers are difficult to understand. It will help the asker and future readers both if you can add more information in your post. – RBT May 10 '17 at 09:22
0

Where items have a Count, this is what I have done:

int Count = myTable.Rows.Count;

while (Count > 0) // replace condition with myTable.Rows.Count if unconditionally performed on all rows
{
    DataRow row = myTable.Rows[0] // or however you want to find your index

    // do some work
    myTable.Rows.Remove(row);

    // if you want to perform a check to break out of while
    if (someCondition)
        Count = 0;
    else
        Count = myTable.Rows.Count;
}

Note, that where objects have a .GetXXXX() collection, like FileInfo (IIRC), deleting item contents in a foreach is acceptable. One solution I have considered is creating an extension method that provides a .GetItems() method.

IAbstract
  • 19,551
  • 15
  • 98
  • 146