Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DataTable throwing exception on RejectChanges

I found this bug while working with a DataTable. I added a primary key column to a DataTable, than added one row to that table, removed that row, and added row with the same key to the table. This works. When I tried to call RejectChanges() on it, I got ConstraintException saying that value is already present. Here is the example:

    var dataTable = new DataTable();
    var column = new DataColumn("ID", typeof(decimal));
    dataTable.Columns.Add(column);
    dataTable.PrimaryKey =  new [] {column };

    decimal id = 1;

    var oldRow = dataTable.NewRow();
    oldRow[column] = id;

    dataTable.Rows.Add(oldRow);
    dataTable.AcceptChanges();

    oldRow.Delete();

    var newRow = dataTable.NewRow();
    newRow[column] = id;

    dataTable.Rows.Add(newRow);
    dataTable.RejectChanges(); // This is where it crashes

I think since the row is deleted, exception should not be thrown (constraint is not violated because row is in deleted state). Is there something I can do about this? Any help is appreciated.

like image 453
Vale Avatar asked Oct 09 '22 08:10

Vale


1 Answers

I assume that this has the same cause than following bug issue since the first that will be rejected is your delete action:

DataTable.RejectChanges() should rollback rows in reverse order

Two possible workarounds:

Cycles through the DataRows rolling them back in reverse order. So the new records are removed before the previous ones are brought back to life.

DataRowCollection rows = dataTable.Rows;
for (int i = rows.Count - 1; i >= 0; i--)
{
    rows[i].RejectChanges();
}

Disables constrains so the rollback can be done. Reenables constrains after that.

  1. You could use LINQ-to-DataSet to define your own "rollback-order":

    var rollbackPlan = (from r in dataTable.AsEnumerable()
                   where r.RowState != DataRowState.Unchanged
                   let firstOrder  = r.RowState==DataRowState.Deleted? 1 : 0
                   let secondOrder = r.RowState==DataRowState.Added?   1 : 0
                   orderby firstOrder ascending, secondOrder ascending
                   select r).ToList();
    foreach (DataRow r in rollbackPlan)
    {
        r.RejectChanges(); // Does not crash anymore
    }
    
  2. Here's the way you "disable" constraints on a DataTable temporarily:

    var constraintBackup = dataTable.Constraints.Cast<System.Data.Constraint>().ToList();
    dataTable.Constraints.Clear();
    dataTable.RejectChanges(); // Does not crash anymore
    foreach (System.Data.Constraint c in constraintBackup)
    {
        dataTable.Constraints.Add(c);
    }
    
like image 165
Tim Schmelter Avatar answered Oct 18 '22 20:10

Tim Schmelter