views:

553

answers:

4

Hello:

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

+2  A: 

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); }
}
SLaks
Thanks for the response. By doing it this way, will there be implications when I try to update my table back to the db? _dt.Update() Will the Update() know to delete this row using the DeleteCommand or is it "gone for good?"
Ken
@Ken: No, removed rows will _not_ be deleted during an update. Remove != Delete
Henk Holterman
@Ken: Good point; I would assume that calling remove will cause `Update` to not delete it. Try it.
SLaks
@SLaks - Calling Remove causes the Update to not delete it in the db. Also, C#3 is not valid for me (using 2.0), but thanks for your input!
Ken
+2  A: 

You will have to check:

 _dt.DefaultView.RowStateFilter

I think that the default setting won't show deleted rows, maybe you change it somewhere.

You can always create an extra RowView and control its filter and do your loop over the View.

Henk Holterman
That won't affect the table's enumerator.
SLaks
@Slaks: I wrote: 'and loop over the View'
Henk Holterman
That loses the typed rows.
SLaks
@slaks: not with the appropriate foreach...
Henk Holterman
@Henk: I ended up looping over the DataView (which by default does not contain the deleted records) like so:`foreach (DataRowView row in _dt.DefaultView ){ sbFilter.Append("'" + ((myDataTableRow)row.Row).info+ "',");}`Out of all the options offered, I think this is the cleanest way. Thanks!
Ken
@Ken: See my edit for a simpler version.
SLaks
A: 

I'm not sure of this, but I think if you call _dt.AcceptChanges() after deleting one or more rows, you will not "see" the deleted rows when you iterate through the data tables's Rows collection.

MusiGenesis
AcceptChanges removes the row state, and I need this state later on when I do a _dt.Update() to persist changes back to the DB. I would probably call AcceptChanges() after the update. Thanks!
Ken
+1  A: 

Use GetChanges():

foreach (myDataTableRow row in _dt.GetChanges(DataRowState.Added))
{
    sbFilter.Append("'" + row.info + "',");
}

If you want to get all added and modified, use bitwise OR on Added and Modified:

foreach (myDataTableRow row in
    _dt.GetChanges(DataRowState.Added | DataRowState.Modified))
{
    sbFilter.Append("'" + row.info + "',");
}
Michael Buen
Right idea, but this actually produces a compiler error because DataTable does not contain a public GetEnumerator() method.
Ken
This won't return unchanged rows.
SLaks
@SLaks: Despite the name GetChanges, you can get unchanged rows, _dt.GetChanges(DataRowSate.Unchanged)
Michael Buen