"Index was out of range" error when editing DataRow

J

Jon Fairchild

"Index was out of range" error when editing DataRow

Our application makes thousands of edits to DataSets without problem,
but occasionally this error appears: "Index was out of range. Must be
non-negative and less than the size of the collection. Parameter name:
index"

I have found one place in the code where I can reproduce the error,
though still intermittently. In this case I am changing the value of
field in a DataRow. This field happens to be a foreign key to a parent
table. The parent table contains "Incidents" and the child table
contains "Devices", and they are related through an "Incident ID".
Here is the code that fails occasionally:

deviceRow["INCIDENT_ID"] = newIncidentID;

My testing indicates that the column name and value are valid. As the
stack trace of the error shows, the problem instead has to do with
changing the row state during the edit:

Exception: System.ArgumentOutOfRangeException
Message: Index was out of range. Must be non-negative and less than
the size of the collection.
Parameter name: index
Source: mscorlib
at System.Collections.ArrayList.get_Item(Int32 index)
at System.Data.DataTable.RecordStateChanged(Int32 record1,
DataViewRowState oldState1, DataViewRowState newState1, Int32 record2,
DataViewRowState oldState2, DataViewRowState newState2)
at System.Data.DataTable.SetNewRecord(DataRow row, Int32
proposedRecord, DataRowAction action, Boolean isInMerge)
at System.Data.DataRow.EndEdit()
at System.Data.DataRow.set_Item(DataColumn column, Object value)
at System.Data.DataRow.set_Item(String columnName, Object value)


Any ideas on what could be causing this?

Thanks,
Jon Fairchild
 
W

William Ryan eMVP

Hi Jon:

Could you post the code block around it? There could be a few things going
on but changing rowstate happens whenver you edit, I don't think that's the
problem. If it works most of the time, I'm wondering if you aren't calling
..Remove or something like that which could be throwing off your counter loop
or something. I can't really tell from this and although the stack trace is
usually helpful, I can't see much there.

Let me know and I'll see what I can come up with.

Cheers,

Bill

--
W.G. Ryan MVP Windows - Embedded

www.devbuzz.com
www.knowdotnet.com
http://www.msmvps.com/williamryan/
 
J

Jon Fairchild

Hi Bill,

Thanks for your response! The relevant code is scattered around so it
is hard to show you a ‘block' per se. But I have collected some code
bits to describe the situation to you.

First, we take an Incident row that was selected by the user in a
grid. We create a view on the Device table to get the Device children
of the selected Incident.

//create a device dataview with an explicit filter to ensure it will
show
//only devices for this incident, taking edits into account
_incidentSet = incidentRow.Table.DataSet;
DataTable deviceTable =
_incidentSet.Tables[RxDb.Tables.IncidentDevices.TableName];
DataView deviceView = new DataView(deviceTable);
string rowFilter = string.Format("{0} = {1}",
RxDb.Tables.IncidentDevices.IncidentID, incidentID);
deviceView.RowFilter = rowFilter;


We open a form that displays this list of Devices in a tree. The form
contains an ‘edit view' control where the user can edit attributes for
the Device currently selected in the tree. An edit view contains
multiple sub-controls called ‘field views', each one bound to a column
in the table. Here is the binding code from within the field view:

//set the current row of the DataTable binding context to this DataRow
_currencyManager = this.BindingContext[dataRow.Table]
as CurrencyManager;

_currencyManager.Position = 0;
DataRowView currentView = _currencyManager.Current as DataRowView;
DataRow currentRow = currentView.Row;
while (!(DataSetConfig.AreSameRows(currentRow, dataRow)))
{
_currencyManager.Position++;
if (_currencyManager.Position > _currencyManager.Count) break;
currentView = _currencyManager.Current as DataRowView;
currentRow = currentView.Row;
}


Also we use a currency manager at the form level to track this
binding:

//get the currency manager for the binding of the DataView in the edit
view
_currencyManager =
deviceEditView.BindingContext[deviceView.Table] as CurrencyManager;


We set the view's filter state to show only current rows, as some of
the tools on the form will remove Devices from the tree by deleting
the Device row.

//filter the device view so that only current rows are visible
//(non-deleted rows)
deviceView.RowStateFilter = DataViewRowState.CurrentRows;


One tool on the form is ‘Save as New Incident'. This tool creates a
new row in the Incident table, and updates the Devices selected in the
tree to be children of the new Incident. The last line below
generates the error:

//get the existing parent incident
DataRow existingIncident = selectedDevices[0].GetParentRow
(RxDb.Relations.Incidents_IncidentDevices);
DataTable incidentTable = existingIncident.Table;

//create a new incident
DataRow newIncident = incidentTable.NewRow();
incidentTable.Rows.Add(newIncident);
int newIncidentID = Convert.ToInt32(newIncident[RxDb.Tables.Incidents.ID]);

foreach(DataRow deviceRow in selectedDevices)
{
//assign device to the new incident
deviceRow[RxDb.Tables.IncidentDevices.IncidentID] = newIncidentID;
}


If the error does not occur, we then proceed to set some default
values on the new Incident row, and show a dialog with its own bound
edit view so the user can edit the new incident.

One thing to note is that the user can cancel the Incident dialog,
thus canceling the ‘Save as new Incident' tool. In this case the
selected Devices stay with their current Incident parent and the new
Incident is deleted. I reproduce this error best by running and
canceling ‘Save as New Incident' a few times. Here is the code that
cleans up when the user cancels:

//quit now if user cancelled
if (result == DialogResult.Cancel)
{
//set devices back
int existingIncidentID =
Convert.ToInt32(existingIncident[RxDb.Tables.Incidents.ID]);
foreach(DataRow deviceRow in selectedDevices)
{
deviceRow[RxDb.Tables.IncidentDevices.IncidentID] =
existingIncidentID;
}

//delete new incident that was created
newIncident.Delete();

return;
}

I have try the following tests, but none of them have made the error
go away:
1. I commented out the code that sets the DataView's row filter state.
2. I removed the use of a DataView completely and worked with a table
only.
3. I commented out all binding and currency manager code.
4. I made the tool reassign Devices to an existing Incident row rather
than a new one.
5. I commented out the code that deletes the new Incident row upon
cancel.

I hope this description of the situation is adequate. Thanks for your
help with this issue!

-Jon Fairchild
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top