Storing Data in Memory in Microsoft ADO.NET 4

  • 10/25/2010

Batch Processing

The features shown previously for adding, modifying, and removing data records within a DataTable all take immediate action on the content of the table. When you use the Add method to add a new row, it’s included immediately. Any field-level changes made within rows are stored and considered part of the record—assuming that no data-specific exceptions get thrown during the updates. After you remove a row from a table, the table acts as if it never existed.

Although this type of instant data gratification is nice when using a DataTable as a simple data store, sometimes it is preferable to postpone data changes or make several changes at once, especially when you need to verify that changes occurring across multiple rows are collectively valid.

ADO.NET includes table and row-level features that let you set up “proposed” changes to be accepted or rejected en masse. When you connect data tables to their external database counterparts in later chapters, ADO.NET uses these features to ensure that updates to both the local copy of the data and the remote database copy retain their integrity. You can also use them for your own purposes, however, to monitor changes to independent DataTable instances.

To use the batch system, simply start making changes. When you are ready to save or reject all changes made within a DataTable, call the table’s AcceptChanges method to commit and approve all pending changes, or call the RejectChanges method to discard all unsaved changes. Each DataRow in the table also includes these methods. You can call the row-level methods directly, but the table-level methods automatically trigger the identically named methods in each modified row.

C#
someTable.AcceptChanges();  // Commit all row changes
someTable.RejectChanges();  // Reject changes since last commit

Visual Basic
someTable.AcceptChanges()  ' Commit all row changes
someTable.RejectChanges()  ' Reject changes since last commit

Row State

While making your row-level edits, ADO.NET keeps track of the original and proposed versions of all fields. It also monitors which rows have been added to or deleted from the table, and can revert to the original row values if necessary. The Framework accomplishes this by managing various state fields for each row. The main tracking field is the DataRow.RowState property, which uses the following enumerated values:

  • DataRowState.Detached The default state for any row that has not yet been added to a DataTable.

  • DataRowState.Added This is the state for rows added to a table when changes to the table have not yet been confirmed. If you use the RejectChanges method on the table, any added rows will be removed immediately.

  • DataRowState.Unchanged The default state for any row that already appears in a table, but has not been changed since the last call to AcceptChanges. New rows created with the NewRow method use this state.

  • DataRowState.Deleted Deleted rows aren’t actually removed from the table until you call AcceptChanges. Instead, they are marked for deletion with this state setting. See the following discussion for the difference between “deleted” and “removed” rows.

  • DataRowState.Modified Any row that has had its fields changed in any way is marked as modified.

Every time you add or modify a record, the data table updates the row state accordingly. However, removing records from a data table with the Rows.Remove and Rows.RemoveAt methods circumvents the row state tracking system, at least from the table’s point of view.

To enable ADO.NET batch processing support on deleted rows, use the DataRow object’s Delete method. This does not remove the row from the DataTable.Rows collection. Instead, it marks the row’s state as deleted. The next time you use the table or row AcceptChanges method to confirm all updates, the row will be removed permanently from the table.

If you want to use the batch processing features, or if your DataTable instances are associated with a database table, even if that table is temporarily disconnected, you need to use the row-specific Delete method instead of Remove and RemoveAt.

C#
someTable.Rows.Remove(oneRow); // Removes row immediately
oneRow.Delete();               // Marks row for removal during approval

Visual Basic
someTable.Rows.Remove(oneRow) ' Removes row immediately
oneRow.Delete()               ' Marks row for removal during approval

If you retain a reference to a deleted row once it has been removed from the table, its RowState property will be set to DataRowState.Detached, just like a new row that has not yet been added to a table.

Row Versions

When you make changes to data within a data table’s rows, ADO.NET keeps multiple copies of each changed value. Row version information applies to an entire row, even if only a single data column in the row has changed.

  • DataRowVersion.Original The starting value of the field before it was changed (the value that was in effect after the most recent use of AcceptChanges occurred).

  • DataRowVersion.Proposed The changed but unconfirmed field value. The Proposed version of a field doesn’t exist until you begin to make edits to the field or row. When changes are accepted, the proposed value becomes the actual (Original) value of the field.

  • DataRowVersion.Current For fields with pending edits, this version is the same as Proposed. For fields with confirmed changes, the Current version is the same as the Original version.

  • DataRowVersion.Default For rows attached to a DataTable, this version is the same as Current. For detached rows, Default is the same as Proposed. The Default version of a row is not necessarily the same as the default values that might appear in newly created rows.

Depending on the current state of a row, some of these row versions might not exist. To determine whether a row version does exist, use the DataRow.HasVersion method. The following code block uses the HasVersion method and a special overload of the Item method to access various row versions:

C#
if (oneRow.HasVersion(DataRowVersion.Proposed))
{
    if (oneRow.Item["Salary", DataRowVersion.Original] !=
            oneRow.Item["Salary", DataRowVersion.Proposed])
        MessageBox.Show("Proposed salary change.");
}

Visual Basic
If (oneRow.HasVersion(DataRowVersion.Proposed) = True) Then
    If (oneRow.Item("Salary", DataRowVersion.Original) <>
            oneRow.Item("Salary", DataRowVersion.Proposed)) Then _
        MessageBox.Show("Proposed salary change.")
End If

The default row version returned by the Item property is the Current version.