Friday, June 11, 2010

About DataRow.Delete, .AcceptChanges and .RejectChanges

See The LoadDataRow and Delete Methods of DataTable for intention.

Purpose

The following 3 tests [ Test 1 | Test 2 | Test 3 ] tell you about in which situation you can roll back your data and when you cannot.

This page is generated by the application developed in C# under Visual 2008 Profession SP + .NET 3.5 SP1 on Windows XP Professional SP3.

Test 1: No AcceptChanges before deletion
  • Set up a DataTable with 2 rows; so the initial RowState of all DataRows is Added.
  • Without AcceptChanges, immediately delete the 2nd DataRow using Delete method.
  • The RowState of the deleted row is not Deleted but Detached; The result is different from what it is said in the documentation [According to MSDN, it is currently supported in: 4, 3.5, 3.0, 2.0, 1.1, 1.0]. Please also see the test foot notes in blue.
  • Then RejectChanges after deletion; unfortunately, the System.Data.RowNotInTableException is received.

EmpIdFirstNameLastNameSalaryLastName and FirstNameRowState
A123456789BettyeWilliams11.00Williams, BettyeAdded
B223456789RexBloomer12.00Bloomer, RexAdded

Initial state: [B223456789, Rex] RowState: Added
Version: Original doesn't not exist.
Version: Current Value: Rex
Version: Proposed doesn't not exist.
Version: Default Value: Rex

Using Delete method to delete [B223456789, Rex] at RowState: Added...
EmpIdFirstNameLastNameSalaryLastName and FirstNameRowState
A123456789BettyeWilliams11.00Williams, BettyeAdded

The deleted row after deletion: RowState: Detached
Version: Original doesn't not exist.
Version: Current doesn't not exist.
Version: Proposed doesn't not exist.
Version: Default doesn't not exist.

This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.
System.Data.RowNotInTableException
at System.Data.DataRow.GetDefaultRecord() at System.Data.DataColumn.CheckNullable(DataRow row) at System.Data.DataColumn.CheckColumnConstraint(DataRow row, DataRowAction action) at System.Data.DataTable.RaiseRowChanging(DataRowChangeEventArgs args, DataRow eRow, DataRowAction eAction, Boolean fireEvent) at System.Data.DataTable.SetNewRecordWorker(DataRow row, Int32 proposedRecord, DataRowAction action, Boolean isInMerge, Int32 position, Boolean fireEvent, Exception& deferredException) at System.Data.DataTable.SetNewRecord(DataRow row, Int32 proposedRecord, DataRowAction action, Boolean isInMerge, Boolean fireEvent) at System.Data.DataTable.RollbackRow(DataRow row) at System.Data.DataRow.RejectChanges() ...

After error: RowState: Detached
Version: Original doesn't not exist.
Version: Current doesn't not exist.
Version: Proposed doesn't not exist.
Version: Default doesn't not exist.

  • By definition of DataRow.Delete method, the row should have been marked as Deleted.
  • According to this, this bug should have been fixed in VS 2008. Apparently, it still exists. Or there may be a typo in the document. Originally it said, "If the RowState of the row is Added, the RowState becomes Detached and the row is removed from the table when you call AcceptChanges." Perhaps, it is meant to say, ""If the RowState of the row is Added, the RowState becomes Detached and the row is removed from the table when you call RejectChanges." Then everything will become and make more sense.
  • Because DataRow.RowState is set to Detached, we cannot roll it back by using RejectChanges.


Test 2: AcceptChanges before deletion
  • Set up a DataTable with 2 rows; so the initial RowState of all DataRows is Added.
  • Then AcceptChanges, which changes the RowState from Added to Unchanged.
  • Delete the 2nd DataRow using Delete method. The RowState of the 2nd DataRow is marked as Deleted now.
  • Immdiately RejectChanges after deletion, which changes the RowState from Deleted back to Unchanged

EmpIdFirstNameLastNameSalaryLastName and FirstNameRowState
A123456789BettyeWilliams11.00Williams, BettyeAdded
B223456789RexBloomer12.00Bloomer, RexAdded

Initial state: [B223456789, Rex] RowState: Added
Version: Original doesn't not exist.
Version: Current Value: Rex
Version: Proposed doesn't not exist.
Version: Default Value: Rex

AcceptChanges. [B223456789, Rex] RowState: Unchanged
Version: Original Value: Rex
Version: Current Value: Rex
Version: Proposed doesn't not exist.
Version: Default Value: Rex

Using Delete method to delete [B223456789, Rex] at RowState: Unchanged...
EmpIdFirstNameLastNameSalaryLastName and FirstNameRowState
A123456789BettyeWilliams11.00Williams, BettyeUnchanged
?????Deleted

The deleted row after deletion: RowState: Deleted
Version: Original Value: Rex
Version: Current doesn't not exist.
Version: Proposed doesn't not exist.
Version: Default doesn't not exist.

RejectChanges:
EmpIdFirstNameLastNameSalaryLastName and FirstNameRowState
A123456789BettyeWilliams11.00Williams, BettyeUnchanged
B223456789RexBloomer12.00Bloomer, RexUnchanged

[B223456789, Rex] RowState: Unchanged
Version: Original Value: Rex
Version: Current Value: Rex
Version: Proposed doesn't not exist.
Version: Default Value: Rex

  • After deletion using Delete method, the RowState is set to Deleted.
  • Thus, RejectChanges can be used to roll back the DataRow to the point where the AcceptChanges method is last called.
  • The RowState of the deleted row is also rolled back to Unchanged. Everything looks exactly like before deletion.

Test 3: AcceptChanges, modification and then deletion
  • Set up a DataTable with 2 rows; so the initial RowState of all DataRows is Added.
  • Then AcceptChanges, which changes the RowState from Added to Unchanged.
  • Start BeginEdit ... EndEdit block to modify the 2nd row. Change the FirstName from Rex to Susan.
  • The expression column LastName and FirstName remains unchanged until the EndEdit method is called while FirstName is modified before calling EndEdit.
  • Then delete the 2nd DataRow using Delete method. The RowState of the 2nd DataRow is marked as Deleted now.
  • Immdiately RejectChanges after deletion, which changes everything back to the state when the AcceptChanges is last called; thus the RowState is rolled back all the way from Deleted to Unchanged

EmpIdFirstNameLastNameSalaryLastName and FirstNameRowState
A123456789BettyeWilliams11.00Williams, BettyeAdded
B223456789RexBloomer12.00Bloomer, RexAdded

Initial state: [B223456789, Rex] RowState: Added
Version: Original doesn't not exist.
Version: Current Value: Rex
Version: Proposed doesn't not exist.
Version: Default Value: Rex

AcceptChanges. [B223456789, Rex] RowState: Unchanged
Version: Original Value: Rex
Version: Current Value: Rex
Version: Proposed doesn't not exist.
Version: Default Value: Rex

BeginEdit and modified the 2nd row:
 row.BeginEdit(); row["FirstName"] = "Susan";

EmpIdFirstNameLastNameSalaryLastName and FirstNameRowState
A123456789BettyeWilliams11.00Williams, BettyeUnchanged
B223456789SusanBloomer12.00Bloomer, RexUnchanged
[B223456789, Susan] RowState: Unchanged
Version: Original Value: Rex
Version: Current Value: Rex
Version: Proposed Value: Susan
Version: Default Value: Susan

After EndEdit:
  row.EndEdit();

EmpIdFirstNameLastNameSalaryLastName and FirstNameRowState
A123456789BettyeWilliams11.00Williams, BettyeUnchanged
B223456789SusanBloomer12.00Bloomer, SusanModified
[B223456789, Susan] RowState: Modified
Version: Original Value: Rex
Version: Current Value: Susan
Version: Proposed doesn't not exist.
Version: Default Value: Susan

Using Delete method to delete [B223456789, Susan] at RowState: Modified...
EmpIdFirstNameLastNameSalaryLastName and FirstNameRowState
A123456789BettyeWilliams11.00Williams, BettyeUnchanged
?????Deleted

The deleted row after deletion: RowState: Deleted
Version: Original Value: Rex
Version: Current doesn't not exist.
Version: Proposed doesn't not exist.
Version: Default doesn't not exist.

RejectChanges:
EmpIdFirstNameLastNameSalaryLastName and FirstNameRowState
A123456789BettyeWilliams11.00Williams, BettyeUnchanged
B223456789RexBloomer12.00Bloomer, RexUnchanged

[B223456789, Rex] RowState: Unchanged
Version: Original Value: Rex
Version: Current Value: Rex
Version: Proposed doesn't not exist.
Version: Default Value: Rex

Interesting of this test is that the "LastName and FirstName" column remains unchanged [Bloomer, Rex] after the FirstName has been modified to Susan. The "LastName and FirstName" column is an Expression column set up by expression that contains the values from both FirstName and LastName columns. Changing FirstName will not refect the change immediately in the "FirstName and LastName" column until EndEdit is called.