I get the following error while I try to delete a row while looping through it.
C#: Collection was modified; enumeration operation may not execute
I've been doing some research for a while, and I've read some similar posts here, but I still haven't found the right answer.
foreach (DataTable table in JobsDS.Tables)
{
foreach (DataRow row in table.Rows)
{
if (row["IP"].ToString() != null && row["IP"].ToString() != "cancelled")
{
string newWebServiceUrl = "http://" + row["IP"].ToString() + "/mp/Service.asmx";
webService.Url = newWebServiceUrl;
string polledMessage = webService.mpMethod(row["IP"].ToString(), row["ID"].ToString());
if (polledMessage != null)
{
if (polledMessage == "stored")
{
removeJob(id);
}
}
}
}
}
any help would be greatly appreciated
Instead of using foreach, use a reverse for loop:
for(int i = table.Rows.Count - 1; i >= 0; i--)
{
DataRow row = table.Rows[i];
//do your stuff
}
Removing the row indeed modifies the original collection of rows. Most enumerators are designed to explode if they detect the source sequence has changed in the middle of an enumeration - rather than try to handle all the weird possibilities of foreaching across something that is changing and probably introduce very subtle bugs, it is safer to simply disallow it.
You cannot modify a collection inside of a foreach around it.
Instead, you should use a backwards for loop.
If you want to remove Elements from a loop on a list of Elements, the trick is to use a for loop, start from the last Element and go to the first Element.
In your example :
int t_size = table.Rows.Count -1;
for (int i = t_size; i >= 0; i--)
{
DataRow row = table.Rows[i];
// your code ...
}
Edit : not quick enough :)
Also, if you depend on the order that you process the rows and a reverse loop does not work for you. You can add the rows that you want to delete to a List and then after you exit the foreach loop you can delete the rows added to the list. For example,
foreach (DataTable table in JobsDS.Tables)
{
List<DataRow> rowsToRemove = new List<DataRow>();
foreach (DataRow row in table.Rows)
{
if (row["IP"].ToString() != null && row["IP"].ToString() != "cancelled")
{
string newWebServiceUrl = "http://" + row["IP"].ToString() + "/mp/Service.asmx";
webService.Url = newWebServiceUrl;
string polledMessage = webService.mpMethod(row["IP"].ToString(), row["ID"].ToString());
if (polledMessage != null)
{
if (polledMessage == "stored")
{
//removeJob(id);
rowsToRemove.Add(row);
}
}
}
}
rowsToRemove.ForEach(r => removeJob(r["ID"].ToString()));
}
Somehow removeJob(id) changes one of the IEnumerables your enumerating (table.Rows or JobsDS.Tables, from the name of the method I guess it would be the latter), maybe via DataBinding.
I'm not sure the backwards for is going to work directly because it seems you're removing an element enumerated in the outer foreach from within the inner foreach. It's hard to tell without more info about what happens in removeJob(id).
Related
I have a code
foreach (DataColumn dataTableCol in this.dataTable.Columns)
{
bool columnFound = false;
foreach (GRTColumnView uiColumn in descriptor.UIColumns)
{
if (dataTableCol.ColumnName.Equals(uiColumn.Name))
{
columnFound = true;
break;
}
}
if (!columnFound)
{
if (this.dataTable.Columns.Contains(dataTableCol.ColumnName))
this.dataTable.Columns.Remove(dataTableCol.ColumnName);
}
}
I want to remove some "things" from collection if they aren't found in another collection.
When I run the above program, I get
Iteration may not execute as collection was modified
"Collection was modified" - Coz the remove must have been hit
What is the way to achieve such a thing then ?
What I can think of is make a note of all the "things" to remove and then
foreach( aThing in all_things_to_remove)
remove_from_collection(aThing)
But above doesn't seem a good way to me, as I have to do another looping and am using extra memory
In this specific case, where you're looping through a small collection containing a few columns, you can just create a new collection (via ToList()), so that you're not iterating over the same collection you're modifying:
foreach (var dataTableCol in dataTable.Columns.Cast<DataColumn>().ToList())
{
...
dataTable.Columns.Remove(dataTableCol.ColumnName);
}
The recommended way, especially if the collection is large, is to enumerate backwards:
for (var i = dataTable.Columns.Count - 1; i >= 0; i--)
{
...
dataTable.Columns.Remove(dataTable.Columns[i].ColumnName);
}
You cannot remove items from a collection while enumerating it with the foreach loop. Make a copy of the collection using Collection.ToArray() and run you foreach on the copy and remove your item from the actual collection.
Since DataTable.Columns does not have ToArray or ToList methods, you could use the CopyTo() method and copy the entire columns to a ColumnsArray.
If you do not want to create a copy, then you can use for loop instead of foreach loop. You could edit you code this way:
for (int i = 0; i < dataTable.Columns.Count; i++)
{
bool columnFound = false;
foreach (GRTColumnView uiColumn in descriptor.UIColumns)
{
if (dataTable.Columns[i].Name.Equals(uiColumn.Name))
{
columnFound = true;
break;
}
}
if (!columnFound)
{
if (this.dataTable.Columns.Contains(dataTableCol.ColumnName))
this.dataTable.Columns.Remove(dataTableCol.ColumnName);
}
}
the other way is lowering the index after removing column.
for (int i = 0; i < datatable.Columns.Count; i++)
{
if (datatable.Columns[i].ColumnName.Contains("Column"))
{
datatable.Columns.RemoveAt(i);
i--;
}
}
if (dt.Columns.Contains("RecordID")){
dt.Columns.Remove("RecordID");
dt.AcceptChanges();
}
I am looping through a array of datarows and when a particular random item is not valid I want to remove that item and get the new total to get another random item.
But when I delete a datarow the datarow does not go away... And yes there is probably a much better way to do this but I am not smart enough to do it..
Instead of removing the row I see this inside
ItemArray = podLps[1].ItemArray threw an exception of type System.Data.RowNotInTableException
//PHASE 1: Get all LPs in the pod and add to collection
List<DataRow> allLps = dtLp.AsEnumerable().ToList();
DataRow[] podLps = allLps.Where(x => x.ItemArray[0].ToString() == loPod).ToArray();
//PHASE 2: Pick a random LP from collection that has valid WAVE1
for (int i = podLps.Count(); i > 0; i--)
{
//Recount items in collection since one may have been removed
int randomIndex = random.Next(podLps.Count());
var randomLpUserId = podLps[randomIndex].ItemArray[1].ToString();
var randomLpWave1 = int.Parse(podLps[randomIndex].ItemArray[2].ToString());
//Get WAVE1 # for selected LP
lpNumberOfLoans = GetNumberOfLoans(session, randomLpUserId);
//check if LP has valid WAVE1 then use this person
if (randomLpWave1 > lpNumberOfLoans)
{
return randomLpUserId;
}
else
{
podLps[randomIndex].Delete();
}
}
look at this example and it should point you in the right direction for removing rows I just tested it and it works
for (int i = myDataTable.Rows.Count - 1; i >= 0; i--)
{
DataRow row = myDataTable.Rows[i]; //Remove
if (myDataTable.Rows[i][0].ToString() == string.Empty)
{
myDataTable.Rows.Remove(row);
}
}
I would suggest to use a List for podLps instead of an array.
Then you can use .RemoveAt as Jaco mentioned (dosn't work for arrays).
DataRow.Delete() just flags the Row to be deleted in the next update of your DataTable.
The easiest method is to convert your array of DataRow[] to a List, call RemoveAt and then convert the list back to an array:
var dest = new List<>(podLps);
dest.RemoveAt(randomIndex);
podLps = dest.ToArray();
how to check datatable row has empty space?
i know how to check datatable row has null or not now i want to know empty row
foreach (DataRow _dr in obj_dt.Rows)
{
if (obj_dt.Rows[0]["Measurement"] != DBNull.Value)
{
combo_mesurmnt.Items.Add(obj_dt.Rows[0]["Measurement"].ToString());
}
}
foreach (DataRow _dr in obj_dt.Rows)
{
if (obj_dt.Rows[0]["Measurement"].tostring() != "")
{
combo_mesurmnt.Items.Add(obj_dt.Rows[0]["Measurement"].ToString());
}
}
try this...,
use IsNullOrEmpty
var rowContent = combo_mesurmnt.Items.Add(obj_dr.Rows[0]["Measurement"].ToString());
if (!IsNullOrEmpty(rowContent))
{
combo_mesurmnt.Items.Add(rowContent);
}
First, you have a mistake in your code:
You access obj_dt.Rows[0], although you probably mean _dr.
Then, there are a few things that could be improved:
You can use DataRow.Field<string>, which is more elegant than ToString if you know that the underlying DB field is of type string.
Now, to answer your question: It depends on what you mean by "empty space".
If you mean "empty", you can just check with String.IsNullOrEmpty:
foreach (DataRow _dr in obj_dt.Rows)
{
string measurement = _dr.Field<String>("Measurement");
if (!String.IsNullOrEmpty(measurement))
combo_mesurmnt.Items.Add(measurement);
}
If strings consisting only of spaces should be filtered as well, the following will work:
foreach (DataRow _dr in obj_dt.Rows)
{
string measurement = _dr.Field<String>("Measurement");
if (measurement != null && measurement.Trim() != "")
combo_mesurmnt.Items.Add(measurement);
}
I'm just getting to grips with some c# after being thrown in at the deep end on a little scripting task.
I'm trying to amend some code that builds a drop-down box on a web page so that it contains a list of only four items, I'm not sure of the syntax required to achieve this but hoping you folks can help.
using (CoreBusinessLayerProxy proxy = CoreBusinessLayerProxy.CreateCoreBusinessLayerProxy(BusinessLayerExceptionHandler))
{
eventTypesTable = proxy.GetEventTypesTable();
}
eventTypes.Items.Add(new ListItem(Resources.CoreWebContent.WEBCODE_VB0_201, "All events"));
EventTypes.Add("All events", 0);
foreach (DataRow r in eventTypesTable.Rows)
{
eventTypes.Items.Add(r["Name"].ToString());
try
{
EventTypes.Add(r["Name"].ToString(), Int32.Parse(r["EventType"].ToString()));
}
catch { }
}
I'm trying to amend the above so that rather than adding all of the DataRow's from eventTypeTable.Rows it adds only a set of four hard-coded values which I suspect I need to pass during the foreach but I cannot for the life of me wrap my head around where or when I should be doing this in the loop.
(The joys of working on uncommented code)
Thanks!
Here's a rudimentary way to do this based on the Name field. If the Name is not one of "allow1", "allow2", "allow3" or "allow4", then it will continue. This moves to the next iteration in the foreach loop without executing the rest of the statement block for the current one.
foreach (DataRow r in eventTypesTable.Rows)
{
string name = r["Name"].ToString();
if ( name != "allow1" && name != "allow2" && name != "allow3" && name != "allow4" )
continue;
eventTypes.Items.Add(r["Name"].ToString());
try
{
EventTypes.Add(r["Name"].ToString(), Int32.Parse(r["EventType"].ToString()));
}
catch { }
}
I want to delete all datatables in a dataset apart from one. I've tried this :-
foreach (DataTable table in DtSet.Tables)
{
if (table.TableName != "tblAccounts")
{
DtSet.Tables.Remove(table);
}
}
but I get a
"Collection was modified; enumeration operation may not execute."
error
.
You cannot modify a collection during enumeration. But you could use a for-loop:
for (int i = DtSet.Tables.Count - 1; i >= 0; i--)
{
var table = DtSet.Tables[i];
if (table.TableName != "tblAccounts")
DtSet.Tables.Remove(table);
}
Just throwing this out there as it is untested but has no loops.
var accounts = DtSet.Tables["tblAccounts"];
DtSet.Tables.Clear();
DtSet.Tables.Add(accounts);
You'll get this anytime you try to modify a collection during a foreach. It has to do with the way the collection is iterated through.
You can do it a couple ways - one way is to determine the tables you want to remove while you're in the loop, then afterward, go back and remove them. I think the following will work:
var tablesToRemove = new List<DataTable>();
foreach (DataTable table in DtSet.Tables)
{
if (table.TableName != "tblAccounts")
{
tablesToRemove.Add(table);
}
}
foreach (DataTable table in tablesToRemove)
{
DtSet.Tables.Remove(table);
}