NDP - ADO.NET 2.0
Editing data in a DataTable

The code below is in project DataSet\DtEdit.

The DataTable class is designed to track all changes that you make to data. This includes

Changes are tracked so that a database can be updated.

This complicates things.

Each DataRow in a DataTable has a RowState property indicating its disposition. Values are from the DataRowState enumeration:

RowStateState Description
Unchanged No changes have been made since the last call to AcceptChanges or since the row was created by DataAdapter.Fill.
Added The row has been added to the table, but AcceptChanges has not been called.
Modified Some element of the row has been changed.
Deleted The row has been deleted from a table and AcceptChanges has not been called.
Detached

Detached is set for a row that has been created but is not part of any DataRowCollection. The RowState of a newly created row is set to Detached. After the new DataRow is added to the DataRowCollection by calling the Add method, the value of the RowState property is set to Added.

Detached is also set for a row that has been removed from a DataRowCollection using the Remove method, or by the Delete method followed by the AcceptChanges method.

            Detached
          / ^       ^ 
        1/ /3,5      \2
        V /           \
     Added  Modified-->Deleted
         \    ^   |        |
         2\  4|   |2,3     |3
           V  |   V        | 
           Unchanged <-----+


1. Add to DataTable
2. AcceptChanges()
3. RejectChanges()
4. Modify
5. Delete

Note that deleted rows stay in the Rows collection until either AcceptChanges() or RejectChanges() is called. This can cause problems. (See below.)

Closely tied to RowState is the DataRowVersion. A DataSet maintains multiple values for each row. This allows RejectChanges() to work and allows updates back to the database.

The different versions are notated by the DataRowVersion enumeration:

DataRowVersion value Description
Current The current values for the row. This row version does not exist for rows with a RowState of Deleted.
Default The default version of DataRowState. For a DataRowState value of Added, Modified or Deleted, the default version is Current. For a DataRowState value of Detached, the version is Proposed. (The 1.x docs had this wrong!)
Original The original values for the row. This row version does not exist for rows with a RowState of Added.
Proposed The proposed values for the row. This row version exists during an edit operation on a row, or for a row that is not part of a DataRowCollection.


In other words:

State\Version Current Original Proposed
Added Yes No If being edited
Modified Yes Yes If being edited
Deleted No Yes No
Detached No No Yes

Suppose we have set up a table using the following:

        static DataSet ds;
        static DataTable items;
     
        
        private static void CreateDataTable()
        {
            ds = new DataSet("fred");

            items = new DataTable("Items");
            ds.Tables.Add(items);

            DataColumn dc;

            dc = new DataColumn();
            dc.ColumnName = "id";
            dc.DataType = typeof(Int32);
            dc.ReadOnly = true;
            dc.AllowDBNull = false;
            dc.Unique = true;
            dc.AutoIncrement = true;
            dc.AutoIncrementSeed = 100;
            dc.AutoIncrementStep = 10;
            items.Columns.Add(dc);

            dc = new DataColumn();
            dc.ColumnName = "name";
            dc.DataType = typeof(string);
            dc.AllowDBNull = false;
            dc.Unique = true;
            items.Columns.Add(dc);

            dc = new DataColumn();
            dc.ColumnName = "color";
            dc.DataType = typeof(string);
            dc.AllowDBNull = true;
            items.Columns.Add(dc);

            items.PrimaryKey = new DataColumn[] { items.Columns["id"] };
        }

        static string[] itemNames = { "widget", "freeble", "thingie", "whatzit" };
        static string[] itemColors = { "red", "blue", "blue", "red", "red" };

        private static void EnterInitialData()
        {

            DataRow row;

            for (int i = 0; i < itemNames.Length; ++i)
            {
                row = items.NewRow();
                row["name"] = itemNames[i];
                row["color"] = itemColors[i];
                items.Rows.Add(row);
            }

            // Make the initial rows Unchanged
            items.AcceptChanges();
        }     

Now suppose we make some modifications and start an edit on a row:

        private static void EditDataTable()
        {
            DataRow row;

            row = items.Rows[0];
            row.Delete();


            row = items.Rows[1];
            row["color"] = "teal";

            row = items.NewRow();
            row["name"] = "thingamabob";
            row["color"] = "green";
            items.Rows.Add(row);

            row = items.Rows[2];
            row.BeginEdit();
            row["color"] = "cyan";

        }
       private static void ViewData()
        {
            foreach (DataRow row in items.Rows)
                PrintRow(row);
        }

        private static void PrintRow(DataRow row)
        {
            Console.WriteLine("-----");
            Console.WriteLine("Row state = {0}", row.RowState);
            foreach (DataRowVersion version in Enum.GetValues(typeof(DataRowVersion)))
                PrintRowVersion(row, version);
        }

        private static void PrintRowVersion(DataRow row, DataRowVersion version)
        {
            if (row.HasVersion(version))
            {
                Console.Write("Version {0}: ", version);
                foreach (DataColumn c in items.Columns)
                    Console.Write("{0} ", row[c, version]);
                Console.WriteLine();
            }
            else
                Console.WriteLine("No version {0}.", version);
        }

The output is:

-----
Row state = Deleted
Version Original: 100 widget red
No version Current.
No version Proposed.
No version Default.
-----
Row state = Modified
Version Original: 110 freeble blue
Version Current: 110 freeble teal
No version Proposed.
Version Default: 110 freeble teal
-----
Row state = Unchanged
Version Original: 120 thingie blue
Version Current: 120 thingie blue
Version Proposed: 120 thingie cyan
Version Default: 120 thingie cyan
-----
Row state = Unchanged
Version Original: 130 whatzit red
Version Current: 130 whatzit red
No version Proposed.
Version Default: 130 whatzit red
-----
Row state = Added
No version Original.
Version Current: 140 thingamabob green
No version Proposed.
Version Default: 140 thingamabob green
Hit enter to continue.
        private static void ViewDataUnsafe()
        {
            try
            {
                Console.WriteLine("Unsafe viewing:");
                foreach (DataRow row in items.Rows)
                {
                    foreach (DataColumn c in items.Columns)
                        Console.Write("{0} ", row[c]);
                    Console.WriteLine();
                }
            }
            catch (DeletedRowInaccessibleException )
            {
                Console.WriteLine("Caught a DeletedRowInaccessibleException.");
            }
        }

        private static void ViewDataSafeYouThink()
        {
            try
            {
                Console.WriteLine("Maybe safe viewing?:");
                foreach (DataRow row in items.Rows)
                {
                    foreach (DataColumn c in items.Columns)
                        Console.Write("{0} ", row[c,DataRowVersion.Default]);
                    Console.WriteLine();
                }
            }
            catch (DeletedRowInaccessibleException)
            {
                Console.WriteLine("Caught a DeletedRowInaccessibleException.");
            }
        }