I have a parent WinForm that has a MyDataTable _dt
as a member. The MyDataTable type was created in the "typed dataset" designer tool in Visual Studio 2005 (MyDataTable inherits from DataTable) _dt
gets populated from a db via ADO.NET. Based on changes from user interaction in the form, I delete a row from the table like so:
_dt.FindBySomeKey(_someKey).Delete();
Later on, _dt
is passed by value to a dialog form. From there, I need to scan through all the rows to build a string:
foreach (myDataTableRow row in _dt)
{
sbFilter.Append("'" + row.info + "',");
}
The problem is that upon doing this after a delete, the following exception is thrown:
DeletedRowInaccessibleException: Deleted row information cannot be accessed through the row.
The work around that I am currently using (which feels like a hack) is the following:
foreach (myDataTableRow row in _dt)
{
if (row.RowState != DataRowState.Deleted &&
row.RowState != DataRowState.Detached)
{
sbFilter.Append("'" + row.info + "',");
}
}
My question: Is this the proper way to do this? Why would the foreach loop access rows that have been tagged via the Delete()
method??
The Delete
method marks a row for deletion; the row is not actually removed until you call AcceptChanges
.
Instead, call
Note that if you call _dt.Rows.Remove(_dt.FindBySomeKey(_someKey))
, which will also accept the change.
Believe it or not, Rows.Remove
will completely remove the row, whereas row.Delete()
won't. Rows.Remove
, the row will be permanently gone, and will not be deleted from the database by a DataAdapter.
In C# 3, you can replace your if
statement with the following extension method:
///<summary>Gets the rows in a typed DataTable that have not been deleted.</summary>
public static EnumerableRowCollection<TRow> CurrentRows<TRow>(this TypedTableBase<TRow> table) where TRow : DataRow {
return table.Where(r => r.RowState != DataRowState.Deleted);
}
foreach (myDataTableRow row in _dt.CurrentRows())
{
...
Here's a C# 2 version:
static class Utils {
public static IEnumerable<TRow> CurrentRows(IEnumerable<TRow> table) where TRow : DataRow {
foreach(TRow row in table) {
if (row.RowState != DataRowState.Deleted)
yield return row;
}
}
}
foreach (myDataTableRow row in Utils.CurrentRows(_dt))
{
You could also put this function in the partial class for the typed table:
partial class MyTable {
public IEnumerable<MyRow> CurrentRows() { return Utils.CurrentRows(this); }
}
What you're doing to skip the rows in your iteration is correct, I routinely check the RowState when I loop over a DataTable that may be modified.
In some cases I believe you want the original row value before the row was marked as deleted. There is a secondary index option when retrieving a particular datarow value.
string st = dr["columnname", DataRowVersion.Original].ToString(); // c#
dim st As String = dr("columnname", DataRowVersion.Original).ToString() ' vb.net
I've gotten stuck on this one several times in the past.
As far as the GetChanges(RowState) method is concerned, don't forget to to check for a null return, if there are no rows of that RowState, the DataTable returned is null (I think it should return a table with zero rows)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With