Deleting specific rows from DataTable - c#

I want to delete some rows from DataTable, but it gives an error like this,
Collection was modified; enumeration operation might not execute
I use for deleting this code,
foreach(DataRow dr in dtPerson.Rows){
if(dr["name"].ToString()=="Joe")
dr.Delete();
}
So, what is the problem and how to fix it? Which method do you advise?

If you delete an item from a collection, that collection has been changed and you can't continue to enumerate through it.
Instead, use a For loop, such as:
for(int i = dtPerson.Rows.Count-1; i >= 0; i--)
{
DataRow dr = dtPerson.Rows[i];
if (dr["name"] == "Joe")
dr.Delete();
}
dtPerson.AcceptChanges();
Note that you are iterating in reverse to avoid skipping a row after deleting the current index.

Before everyone jumps on the 'You can't delete rows in an Enumeration' bandwagon, you need to first realize that DataTables are transactional, and do not technically purge changes until you call AcceptChanges()
If you are seeing this exception while calling Delete, you are already in a pending-changes data state. For instance, if you have just loaded from the database, calling Delete would throw an exception if you were inside a foreach loop.
BUT! BUT!
If you load rows from the database and call the function 'AcceptChanges()' you commit all of those pending changes to the DataTable. Now you can iterate through the list of rows calling Delete() without a care in the world, because it simply ear-marks the row for Deletion, but is not committed until you again call AcceptChanges()
I realize this response is a bit dated, but I had to deal with a similar issue recently and hopefully this saves some pain for a future developer working on 10-year-old code :)
P.s. Here is a simple code example added by 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()

with this solution:
for(int i = dtPerson.Rows.Count-1; i >= 0; i--)
{
DataRow dr = dtPerson.Rows[i];
if (dr["name"] == "Joe")
dr.Delete();
}
if you are going to use the datatable after deleting the row, you will get an error. So what you can do is:
replace dr.Delete(); with dtPerson.Rows.Remove(dr);

This works for me,
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"); //name is the column in the data table
foreach(var drow in dtr)
{
drow.Delete();
}
dtperson.AcceptChanges();

To remove entire row from DataTable , do like this
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);

Or just convert a DataTable Row collection to a list:
foreach(DataRow dr in dtPerson.Rows.ToList())
{
if(dr["name"].ToString()=="Joe")
dr.Delete();
}

Where is the problem: It is forbidden to delete items from collection inside a foreach loop.
Solution: Either do it like Widor wrote, or use two loops. In the first pass over DataTable you only store (in a temporary list) the references to rows you want to delete. Then in the second pass over your temporary list you delete those rows.

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

I know this is, very, old question, and I have similar situation few days ago.
Problem was, in my table are approx. 10000 rows, so looping trough DataTable rows was very slow.
Finally, I found much faster solution, where I make copy of source DataTable with desired results, clear source DataTable and merge results from temporary DataTable into source one.
note : instead search for Joe in DataRow called name You have to search for all records whose not have name Joe (little opposite way of searching)
There is example (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
I hope so this shorter solution will help someone.
There is c# code (not sure is it correct because I used online converter :( ):
//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;
Of course, I used Try/Catch in case if there is no result (for example, if Your dtPerson don't contain name Joe it will throw exception), so You do nothing with Your table, it stays unchanged.

You try this for getting and removing id column from data table
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");
}

I'm seeing various bits and pieces of the right answer here, but let me bring it all together and explain a couple of things.
First of all, AcceptChanges should only be used to mark the entire transaction on a table as being validated and committed. Which means if you are using the DataTable as a DataSource for binding to, for example, an SQL server, then calling AcceptChanges manually will guarantee that that the changes never get saved to the SQL server.
What makes this issue more confusing is that there are actually two cases in which the exception is thrown and we have to prevent both of them.
1. Modifying an IEnumerable's Collection
We can't add or remove an index to the collection being enumerated because doing so may affect the enumerator's internal indexing.
There are two ways to get around this: either do your own indexing in a for loop, or use a separate collection (that is not modified) for the enumeration.
2. Attempting to Read a Deleted Entry
Since DataTables are transactional collections, entries can be marked for deletion but still appear in the enumeration. Which means that if you ask a deleted entry for the column "name" then it will throw an exception.
Which means we must check to see whether dr.RowState != DataRowState.Deleted before querying a column.
Putting it all together
We could get messy and do all of that manually, or we can let the DataTable do all the work for us and make the statement look and at more like an SQL call by doing the following:
string name = "Joe";
foreach(DataRow dr in dtPerson.Select($"name='{name}'"))
dr.Delete();
By calling DataTable's Select function, our query automatically avoids already deleted entries in the DataTable. And since the Select function returns an array of matches, the collection we are enumerating over is not modified when we call dr.Delete(). I've also spiced up the Select expression with string interpolation to allow for variable selection without making the code noisy.

I have a dataset in my app and I went to set changes (deleting a row) to it but ds.tabales["TableName"] is read only. Then I found this solution.
It's a wpf C# app,
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);
}
}

the easy way use this in button :
var table = $('#example1').DataTable();
table.row($(`#yesmediasec-${id}`).closest('tr')).remove( ).draw();
example1 = id table .
yesmediasec = id of the button in the row
use it and every thing will be ok

Related

Updating a field in a changed row collection does not update underlying table in C#

I have a dataset with table in, and basically I call
DataTable aTable = dataSet.Tables["DocData"].GetChanges();
foreach (DataRow aRow in aTable.Rows)
{
switch (aRow.RowState)
{
case DataRowState.Unchanged:
break;
case DataRowState.Added:
aRow["ID"] = getNewID();
//...updates database here
aRow.AcceptChanges();
break;
}
}
Now the change to the ID on the changed row is not updated in the underlying table object the changed row came from... how can I make this happen? I need to add the id when a new row is saved.
Calling GetChanges loops over your rows to retrieve every changed row and builds a copy of that rows. Thus your update changes the copy not the original rows.
It seems that you couldn't avoid to have a loop somewhere to retrieve the added rows so, you could try with this simpler code, thanks to Linq.
foreach (DataRow aRow in aTable.AsEnumerable()
.Where(x => x.RowState == DataRowState.Added)
{
aRow["ID"] = getNewID();
}

Iterate Foreach loop in accordance with first row to get all recors of Table

In following code i want read dataset records for only once to avoid database transaction and want to iterate loop in accordance with first row.
//InitializeConfig method return dataset which contains 6 records
// i.e. Table with 6 rows and 6 columns
dsConfig = objConfig.InitializeConfig(objConfig, NoOfGates);
foreach (DataRow dr in dsConfig.Tables[0].Rows)
{
objCR.GetCard(objConfig.iIndex, objConfig.iCRNo, out MainId);
}
but I am getting always records from first rows only for each iteration.
The question is rather unclear, but let me expand on the comment from venerik. It is pointless to iterate over rows when you're not using them inside the loop body.
foreach (DataRow dr in dsConfig.Tables[0].Rows)
{
// starting from here the variable dr is available
Console.WriteLine(dr["GateNo"].ToString()) // this should print the value of GateNo column for each row
objCR.GetCard(objConfig.iIndex, objConfig.iCRNo, out MainId);
}
Here No need to use Foreach loop simply I can Iterate for loop for my dataset Object
for(i=0; i<dsConfig.Rows.Count; i++)
{
objCR.GetCard(Convert.ToInt32(dsConfig.Tables[0].Rows[i].ItemArray[2]), Convert.ToInt32(dsConfig.Tables[0].Rows[i].ItemArray[3]), out MainId);
}
Because My iIndex and iCRNo parameters from dataset passed to GetCard Method.

Delete a specific row from datatable

I am trying to delete a specific row from datatable. When adding the last row, I need to delete the yellow colored rows. It is very easy to Select from a datatable like below
DataRow[] dr = dt.Select("STOK_KODU='HAMMADDE_2'");
I was wondering if there is a way like below to delete ??? Or would you advice an easy way to delete a rows from datatable?
dt.Delete("STOK_KODU='HAMMADDE_2'");
One way is to recreate the table with the rows you want to keep:
dt = dt.AsEnumerable()
.Where(row => row.Field<string>("STOK_KODU") != "HAMMADDE_2")
.CopyToDataTable()
The other is to use DataRowCollection.Remove:
DataRow[] rowsToRemove = dt.Select("STOK_KODU='HAMMADDE_2'");
foreach (var rowToDelete in rowsToRemove)
dt.Rows.Remove(rowToDelete);
The second approach is more efficient if you want to delete few rows and the table is large. The first approach using LINQ is more powerful since you can use any code but it can be less efficient.
You can access the Rows collection of the DataTable:
foreach (var row in dr)
dt.Rows.Remove(row);
try this ::
DataRow[] rows;
rows=dt.Select("STOK_KODU='HAMMADDE_2'");
foreach(DataRow r in rows)
r.Delete();
Deleting rows from an in memory DataTable object is really easy
dt.Select("STOK_KODU='HAMMADDE_2'").AsEnumerable().ToList().ForEach(x => x.Delete());
However you should consider that the Delete method simply marks the RowState to Deleted, but the rows are still in the DataTable.Rows collection. To really remove them you need to call
dt.AcceptChanges();
without this call, if you loop over the datatable rows collection, you need to check the RowState to avoid an error message stating that you cannot access the information of a deleted row
foreach(DataRow r in dt.Rows)
{
if(r.RowState != DataRowState.Deleted)
Console.WriteLine(r[0].ToString());
}

How to remove rows from huge data table without iterating it?

I have a DataTable available with me which contains thousands of rows. There is a column called EmpID which is containing '0' for some of the rows. I want to remove them from my current DataTable and want to create a new correct DataTable. I cannot go row by row checking it since it contains huge amount of data. Give me a suggestion to overcome this problem.
the best way would be to filter it at source (if possible) - so if you are creating it from a db, exclude all 0 values in your sql query itself using a where
starting .net 2.0, ms enhanced the filtering logic on the datatable to a great extent. so if you used the dataview (on top of your datatable) and added the where clause in there and added some sort of runtime indexes on this field, it would give you the desired results without looping over all records
You can use DataTable.Select("EmpID <> 0"). This will return an array of DataRows which you can create your new DataTable from if required.
Isn't it possible to first select the rows with EmpID = 0 and then iterate over these only ?
DataTable newTable = new DataTable();
foreach (DataRow dr in oldTable.Select("EmpID = '0'")) {
newTable.Rows.Add(dr);
oldTable.Rows.Remove(dr);
}
You can try
DataRow[] temp=
table.Select("EmpID ='0'");
foreach(DataRow dr in temp)
{
table.Rows.Remove(dr);
}
table.acceptchanges();

Remove row from generic datatable in C#

I ran into a problem trying to remove a row from a datatable in C#. The problem is that the datatable is built from SQL, so it can have any number of columns and may or may not have a primary key. So, I can't remove a row based on a value in a certain column or on a primary key.
Here's the basic outline of what I'm doing:
//Set up a new datatable that is an exact copy of the datatable from the SQL table.
newData = data.Copy();
//...(do other things)
foreach (DataRow dr in data.Rows)
{
//...(do other things)
// Check if the row is already in a data copy log. If so, we don't want it in the new datatable.
if (_DataCopyLogMaintenance.ContainedInDataCopyLog(dr))
{
newData.Rows.Remove(dr);
}
}
But, that gives me an error message, "The given DataRow is not in the current DataRowCollection". Which doesn't make any sense, given that newData is a direct copy of data. Does anyone else have any suggestions? The MSDN site wasn't much help.
Thanks!
Your foreach needs to be on the copy, not the original set. You cannot remove an object contained in collection1 from collection2.
foreach (DataRow dr in newData.Rows)
Otherwise you could use a counter to remove at an index. Something like this:
for(int i = 0; i < data.Rows.Count; i++)
{
if (_DataCopyLogMaintenance.ContainedInDataCopyLog(data.Rows[i]))
{
newData.Rows.RemoveAt(i);
}
}

Categories