0

I have some sort of undo functionality in my application. I remove a row from the GUI an could undo or commit it through the underlaying DataTable-functions RejectChanges or AcceptChanges. But when I try to remove the row and undo it via the RejectChanges-functions it does not work, because GetChanges copies the table with its rows and is not returning the acutal DataRow from the original DataTable. Is there a possibility to access the original row through the DataTable returned by GetChanges()? I need to only reject some rows by the DataRow.RejectChanges()-function, so I cannot use DataTable.RejectChanges(). The only thing I found is the Find()-function via the tables primary key. Is there another possibility?

EDIT: I tried the Find()-function and it would not work either, because using the original value from the undo-row cannot find the row in the original table, because this row is marked as deleted.

DataTable originalTable = GetTable();

DataTable del = originalTable.GetChanges(DataRowState.Deleted);
if (del != null) 
{
    foreach (DataRow r in del.Rows) 
    {
        r.RejectChanges(); // not working, bc its the row of 'del'
        DataRow orig = originalTable.Rows.Find(r["id", DataRowVersion.Original]); // not found, because in the current version of originalTable it is not existing
        orig?.RejectChanges();
    }    
}
4
  • A DataTable is not an Undo/Redo container. There's no such built-in container because the functionality is very application-specific. need to only reject some rows why? That's what blocks you. In any case, the xxxChanges methods are intended to implement transaction semantics, not versioning. Row Versions are used to return the original, current or proposed values of a row and are intended for optimistic concurrency. Commented Aug 13 at 14:51
  • The DataTable offers simple commit- or undo-mechanisms and they work fine. I cannot simply do something like undo every step like an editor, I can commit or reject changes since the last commit and thats what I need. To answer you question: I delete rows in the UI, do some database-actions for every row and only if this has been succeeded I really delete the row from the DataTable and UI otherwise I undo/reject it to reinitiate the original state. Commented Aug 14 at 8:58
  • The key word is "simple", but only for its intended scenario - displaying database data in a form or editable grid, potentially cancelling local edits before save and persisting the changes before finalizing the local copy. You'll have to adapt to it. The answer shows the simple way of checking a row's state and possibly reversing changes. The GetChanges docs shows how to get the pending changes and persist them, only committing changes to the grid after a successful save. Commented Aug 14 at 9:09
  • And given how old DataTable is, the docs and articles you find will be from the 2000s-2010s. It doesn't mean they're old or outdated - DataTable itself went out of fashion in the 2010s when ORMs gained in popularity and there were no new developments. It even didn't make it in the first versions of .NET Core, until MS devs realized a lot of scenarios do need a loosely-typed table-like data structure. Commented Aug 14 at 9:12

1 Answer 1

2

You can try something like this:

        private void RejectChangesOnDeletedRows(DataTable dt)
        {
            for (int i = 0; i < dt.Rows.Count;  i++)
            {
                DataRow dr = dt.Rows[i];
                if (dr.RowState == DataRowState.Deleted)
                {
                    // add your own additional logic for extra conditions as required. 

                    dr.RejectChanges(); 
                }
            }
        }

Code to test:

        private void button14_Click(object sender, EventArgs e)
        {
            DataTable dt = new DataTable("abc");
            var dc = new DataColumn("id", typeof(int));
            dt.Columns.Add(dc);
            dt.Columns.Add(new DataColumn("name", typeof(string)));
            dt.PrimaryKey = new DataColumn[] { dc };

            dt.Rows.Add(1, "one");
            dt.Rows.Add(2, "two");
            dt.Rows.Add(3, "three");

            dt.AcceptChanges();

            // writing out the diffgram as a file allows to you more easily see the RowStates,
            // which can help to figure out what is happening.
            dt.WriteXml("c:\\temp\\dt1.xml", XmlWriteMode.DiffGram);

            // below, dgv is a DataGridView. You can hook up to a grid if you want to see the not deleted rows.
            // dgv.DataSource = dt;  

            MessageBox.Show("about to delete");

            DataRow dr = dt.Rows.Find(2);  // find by primary key column.
            dr.Delete();

            dt.WriteXml("c:\\temp\\dt2.xml", XmlWriteMode.DiffGram); // dt2.xml should show deleted rows in diffr:before section.

            MessageBox.Show("about to undo delete");

            RejectChangesOnDeletedRows(dt);

            dt.WriteXml("c:\\temp\\dt3.xml", XmlWriteMode.DiffGram);  // dt3.xml should look the same as dt1.xml. 

            MessageBox.Show("undeleted");
        }
Sign up to request clarification or add additional context in comments.

1 Comment

Yes that works, thanks. I could have thought about this by myself to just loop the original table, because the related rows are marked via the DataRowState. To really delete the row and to not undo I just can AcceptChanges() on the row where its DataRowState.Deleted.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.