Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing deleted rows from a DataTable

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??

like image 477
Ken Avatar asked Apr 26 '10 14:04

Ken


2 Answers

The Delete method marks a row for deletion; the row is not actually removed until you call AcceptChanges.

Instead, 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.
Note that if you call 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())
{
    ...

EDIT

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); }
}
like image 131
SLaks Avatar answered Oct 19 '22 13:10

SLaks


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)

like image 36
DragonZero Avatar answered Oct 19 '22 11:10

DragonZero