none
More information on AcceptRejectRule.Cascade RRS feed

  • Question

  • I've been testing a few senarios with AcceptRejectRule.Cascade and I got few unexpected behaviors and I wanted to be sure everything was working fine. I haven't found much information on AcceptRejectRule.Cascade other than that it will cascade the accept on corresponding rows. Is it possible to have more details on was is a "Corresponding Row" ?

    I've done those tests and it was not behaving like I would expect :

     

    I have 2 tables with 2 columns. The column 1 in each table is the key and they have a relationship base on the column 1. This relationship is set to cascade for Delete, Updates and Accept/Reject.

     

    I inserted a record in both table 1 and table 2 with the same value in column 1 so they are linked together.

     

    If I delete the record in table 2 (the child) and I call the AcceptChanges method on table 1 record, the row from table 2 is not removed from the table like I think it would.

    If I delete the record in table 2 (the child) and I update the key value in table 1 and they try to do a RejectChanges on table 2 record, it will generate an exception since the parent row doesn't have the same key value anymore. I would expect the foreign key value in table 2 row to change event if it is deleted to be able to reject the changes on table 2 row without having to reject them on table 1 row.

     

    I understand this probably have been done in purpose and I just wanted to confirm what is a "Corresponding row" so I can adapt my code to have the behavior I'm looking for.

    Wednesday, September 8, 2010 3:04 PM

Answers

  • Thank you for your detailed response.  Now I understand better.  Usually DataSet is used with a database.  Calling AcceptChanges manually in this scenario is a common beginner problem.  There tends to be a lot of new .NET programmers on these forums, so I hope you'll appreciate why I was quick to reach for that conclusion.

    > If I delete the record in table 2 (the child) and I call the AcceptChanges method on table 1 record, the row from table 2 is not removed from the table like I think it would.

    I have verified what you described with a short program.  Sometimes, if you want something done right, you have to do it yourself :) I just put this together, so be aware that it has not been tested much.  I would recommend leaving AcceptRejectRule as None.

    public static void AcceptChangesRecursively(DataRow row)
    {
      if (row == null)
        throw new ArgumentNullException("row");
      AcceptChangesRecursivelyCore(row, new HashSet<DataRow>());
    }
    
    private static void AcceptChangesRecursivelyCore(DataRow row, HashSet<DataRow> alreadyVisited)
    {
      if (!alreadyVisited.Add(row))
        return; // Cycle detected.
    
      foreach (DataRelation rel in row.Table.ChildRelations)
      {
        foreach (DataRow relRow in row.GetChildRows(rel, DataRowVersion.Original))
          if (relRow.RowState == DataRowState.Deleted)
            AcceptChangesRecursively(relRow);
    
        foreach (DataRow relRow in row.GetChildRows(rel))
          AcceptChangesRecursively(relRow);
      }
    
      row.AcceptChanges();
    }
    

    > If I delete the record in table 2 (the child) and I update the key value in table 1 and they try to do a RejectChanges on table 2

    You need to reject all of the related records, or figure out how to patch it up manually.

     

    • Marked as answer by Serge Vachon Sunday, September 12, 2010 3:40 PM
    Friday, September 10, 2010 1:27 AM

All replies

  • I think you are confusing AcceptRejectRule with the other rules (UpdateRule and DeleteRule).

    The default for AcceptRejectRule is None.  This is what I always use, and I can't think of a good reason to change this.  AcceptChanges generally is uesd to inform the DataSet that the database that the inserts, updates, and deletes have been sent to the database.  When configured the default way, table/data adapters automatically call AcceptChanges when needed.  The cascading of AcceptChanges is inappropriate because it does not make sense to do the accept processing on rows that have not yet been processed against the database.

    > If I delete the record in table 2 (the child) and I call the AcceptChanges method on table 1 record, the row from table 2 is not removed from the table like I think it would.

    You should use DeleteRule of Cascade to accomplish this.  You must set the radio button Foreign Key Constraint Only or Both Relation and Foreign Key Constraint.  I would recommend Both Relation and Foreign Key Constraint.  Calling AcceptChanges or using AcceptRejectRule other than None would be very unusual for this scenario.

    After you perform the delete, you must submit the DataSet to its TableAdapterManager or run the adapters for the affected parent and child table(s).

    To summarize:  AcceptRejectRule.None is almost always what you want.  Furthermore, calling AcceptChanges directly in your code is usually a sign of trouble because adapters do it automatically at the right times.  On the other hand, UpdateRule and DeleteRule are useful to Cascade when used with a ForeginKeyConstraint.

     

    Thursday, September 9, 2010 11:53 PM
  • > I think you are confusing AcceptRejectRule with the other rules (UpdateRule and DeleteRule).

    I am not confusing. I don't want to explain all the application but the basic is that we edit some sort of SQL reports stored in a dataset. Each dataset represents an XML file that you can load in the application. Those report can be edit through a form in which you can modify their properties. They have a lot of properties. When the user click OK or Apply in the form I want to accept the changes from the report (which is not the entire dataset). When the user click Cancel I want to reject the changes from this specific report. The form to edit reports are not modal so I can't call AcceptChanges on the dataset since there might be some modifications in other reports that I don't want to be accepted.

    > The default for AcceptRejectRule is None.  This is what I always use, and I can't think of a good reason to change this.  AcceptChanges generally is uesd to inform the DataSet that the database that the inserts, updates, and deletes have been sent to the database.  When configured the default way, table/data adapters automatically call AcceptChanges when needed.  The cascading of AcceptChanges is inappropriate because it does not make sense to do the accept processing on rows that have not yet been processed against the database.

    Our dataset is not bind to a Database. The reason we are using a dataset is that we can easily write an XML file with it and because I supports transaction with Accept and Reject. The reason I want to cascade the AcceptChanges is that I won't need to accept the changes from all child myself in the code. The user can modify a lot of information in one "Report" and its children and I only want to call the AcceptChanges method on the report so everything is accepted in children.

    > > If I delete the record in table 2 (the child) and I call the AcceptChanges method on table 1 record, the row from table 2 is not removed from the table like I think it would.

    > You should use DeleteRule of Cascade to accomplish this.  You must set the radio button Foreign Key Constraint Only or Both Relation and Foreign Key Constraint.  I would recommend Both Relation and Foreign Key Constraint.  Calling AcceptChanges or using AcceptRejectRule other than None would be very unusual for this scenario.

    It's already done that way. I think you do not understand the scenario I'm describing. I have a parent (Report) which have children (Displayed Columns). If I delete one of the children (The columns) the row is properly switched to Delete state. The problem that I have is that if I call AcceptChanges on the parent (The report), the child row (Columns) stay with a Deleted state and is not removed from the dataset which is not what I want. The question I'm asking is : Is that the normal behavior? If so, I'll manage myself the destruction of children row in the dataset.

    > After you perform the delete, you must submit the DataSet to its TableAdapterManager or run the adapters for the affected parent and child table(s).

    > To summarize:  AcceptRejectRule.None is almost always what you want.  Furthermore, calling AcceptChanges directly in your code is usually a sign of trouble because adapters do it automatically at the right times.  On the other hand, UpdateRule and DeleteRule are useful to Cascade when used with a ForeginKeyConstraint.

    The application we created use the dataset only for a storing purpose. We are not bind to a Database and we only want an easy way to store information and write it to an XML file.

    I hope those information will help you to understand more my problem.

     

    The only question I wanted to be answered is : Is the 2 scenarios describe in my post are normal behavior ? I guess it is probably the case and I will have to handle those 2 scenarios myself if I want those to be handled the way I want.

     

    Thanks

    Friday, September 10, 2010 12:41 AM
  • Thank you for your detailed response.  Now I understand better.  Usually DataSet is used with a database.  Calling AcceptChanges manually in this scenario is a common beginner problem.  There tends to be a lot of new .NET programmers on these forums, so I hope you'll appreciate why I was quick to reach for that conclusion.

    > If I delete the record in table 2 (the child) and I call the AcceptChanges method on table 1 record, the row from table 2 is not removed from the table like I think it would.

    I have verified what you described with a short program.  Sometimes, if you want something done right, you have to do it yourself :) I just put this together, so be aware that it has not been tested much.  I would recommend leaving AcceptRejectRule as None.

    public static void AcceptChangesRecursively(DataRow row)
    {
      if (row == null)
        throw new ArgumentNullException("row");
      AcceptChangesRecursivelyCore(row, new HashSet<DataRow>());
    }
    
    private static void AcceptChangesRecursivelyCore(DataRow row, HashSet<DataRow> alreadyVisited)
    {
      if (!alreadyVisited.Add(row))
        return; // Cycle detected.
    
      foreach (DataRelation rel in row.Table.ChildRelations)
      {
        foreach (DataRow relRow in row.GetChildRows(rel, DataRowVersion.Original))
          if (relRow.RowState == DataRowState.Deleted)
            AcceptChangesRecursively(relRow);
    
        foreach (DataRow relRow in row.GetChildRows(rel))
          AcceptChangesRecursively(relRow);
      }
    
      row.AcceptChanges();
    }
    

    > If I delete the record in table 2 (the child) and I update the key value in table 1 and they try to do a RejectChanges on table 2

    You need to reject all of the related records, or figure out how to patch it up manually.

     

    • Marked as answer by Serge Vachon Sunday, September 12, 2010 3:40 PM
    Friday, September 10, 2010 1:27 AM
  • So, the AcceptChanges is only called on "non-deleted" children right ? And this is the normal behavior of the dataset ? If so, I'll be able to apply a patch to my code so it work as I intended.

     

    Thanks

    Friday, September 10, 2010 11:36 AM
  • > And this is the normal behavior of the dataset ?

    AcceptRejectRule doesn't seem to be documented that thoroughly, so I can't say whether it is a bug or not.  I get the feeling they left out the equivalent of the DataRowVersion.Original recursion that I put in my sample code.  They may have had a reason... I'd say 70% chance of bug.  But no matter, it isn't too bad to work around.

     

     

     

    Friday, September 10, 2010 11:37 PM