New Posts New Posts RSS Feed: Constraint Error in Checkpointing
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

Constraint Error in Checkpointing

 Post Reply Post Reply
Author
aschaff View Drop Down
Newbie
Newbie
Avatar

Joined: 28-May-2009
Posts: 13
Post Options Post Options   Quote aschaff Quote  Post ReplyReply Direct Link To This Post Topic: Constraint Error in Checkpointing
    Posted: 19-May-2011 at 10:18am
I have a tough problem, and I desperately hope you can help me.

I am writing a form where users can edit a report and its parameters. fReport is an entity that can have zero or more fReportParame entities. The primary key of fReport is ReportName. The primary key of fReportParam is (ReportName,UnqiueID). Note: UniqueID, despite its name, is only unique for a given ReportName.

The calling form does a BeginCheckpoint and then does either a SaveChanges or a RollbackCheckpoint based on whether the edit form returns OK or Cancel.

The edit form contains contains data bound report fields as well as an editable grid that I fill with the parameter entities for the particular report. The user can add or remove these parameters. They can also use Up/Down buttons to move the selected row up or down in the grid. Here is where it gets tricky. The grid is sorted by the UnqiueID column. When Up or Down is pressed, I swap the UnqiueID value of the selected entity with the one above or below it accordingly.

Now supposing the parameter grid looks like this:

UnqiueID  ParamName
0         A
1         B
2         C

If I select the first row and click the Down button, the IDs swap and I get this:

UnqiueID  ParamName
0         B
1         A
2         C

Great. However, when I go to save it, it tries to change the UnqiueID of B to 0, which causes a duplicate error because A hasn't been updated and is still 0 in the database.

No problem. I work around that. Since there are never more than 10 parameters for a report, I solve this by adding 100 to the UnqiueID of all the parameters before saving, then subtracting 100 from all of them and saving again. It works, I no longer get a database error during saving. However, I now get a similar problem with these steps:

1. edit a report
2. swap the UnqiueIDs of 0 and 1
3. hit OK which closes the edit window and performs the SaveChanges (with the +/- 100 game)
4. edit the same report again
5. swap the UnqiueIDs of 0 and 1
6. swap the UnqiueIDs of 1 and 2

It all works until step 6 at which point I get the error (included at bottom, note that 'asdf' is the report name I'm testing with). It appears to have something to do with the checkpointing.

Here is the code I use for the Down button (obviously, the Up button is quite similar):

      'find the two rows involved
      Dim param1 As fReportParam = mReport.fReportParams(mParametersGridView.GetDataSourceRowIndex(mParametersGridView.FocusedRowHandle))
      Dim param2 As fReportParam = mReport.fReportParams(mParametersGridView.GetDataSourceRowIndex(mParametersGridView.FocusedRowHandle + 1))
      Dim param1ID As Integer = param1.UniqueID
      Dim param2ID As Integer = param2.UniqueID
     
      'swap their unique IDs
      param2.UniqueID = 99 'otherwise next line gets rejected due to unique constraint
      param1.UniqueID = param2ID '!!!THIS IS WHERE THE ERROR OCCURS!!!!
      param2.UniqueID = param1ID

As you can see, I am guarding against having 2 parameters with the same UniqueID at any point in time. However, I think that the checkpointing code is storing copies of the original values somewhere and when I did step 5 it stored copies of 0 and 1, and when I did step 6 it tried to store copies of 1 and 2 and errored because 1 was changed once previously and it is supposed to be unique. Am I on track? If so, do you have any ideas how I can work around this? I realize that changing the PK of an entity is troublesome, but I don't have the luxury of changing the data model. :(

Here is the error:

 
Back to Top
aschaff View Drop Down
Newbie
Newbie
Avatar

Joined: 28-May-2009
Posts: 13
Post Options Post Options   Quote aschaff Quote  Post ReplyReply Direct Link To This Post Posted: 19-May-2011 at 10:20am
hmm, err didn't come through as image. Here it is as text:

System.Data.ConstraintException: Column 'ReportName, UniqueID' is constrained
     to be unique.  Value 'asdf, 1' is already present.
        at System.Data.UniqueConstraint.CheckConstraint(DataRow row,

     DataRowAction action)
        at System.Data.DataTable.RaiseRowChanging(DataRowChangeEventArgs args,

     DataRow eRow, DataRowAction eAction, Boolean fireEvent)
        at System.Data.DataTable.InsertRow(DataRow row, Int64 proposedID)

        at System.Data.DataTable.AddRecords(Int32 oldRecord, Int32 newRecord)
        at System.Data.DataTable.ImportRow(DataRow row)

        at IdeaBlade.Persistence.Checkpoint.BackupEntity(Entity pEntity)
        at System.Data.DataTable.OnRowChanging(DataRowChangeEventArgs args,

     DataRow eRow, DataRowAction eAction)
        at System.Data.DataTable.RaiseRowChanging(DataRowChangeEventArgs args,

     DataRow eRow, DataRowAction eAction)
        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.DataRow.EndEdit()
        at System.Data.DataRow.set_Item(DataColumn column, Object value)

        at IdeaBlade.Persistence.Entity.SetColumnValue(DataColumn pSetColumn,
     Object pValue)

        at Casco.SV.Model.fReportParamDataRow.SetUniqueIDImpl(Int32 value)
        at Casco.SV.Model.fReportParamDataRow.set_UniqueID(Int32 Value)

        at
     Casco.SV.OmNet.MaintReports.EditReportForm.mMoveDownOfficeButton_Click(

     Object sender, EventArgs e)
        at System.Windows.Forms.Control.OnClick(EventArgs e)

        at DevExpress.XtraEditors.BaseButton.OnClick(EventArgs e)
        at DevExpress.XtraEditors.BaseButton.OnMouseUp(MouseEventArgs e)

        at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons
     button, Int32 clicks)

        at System.Windows.Forms.Control.WndProc(Message& m)
        at DevExpress.Utils.Controls.ControlBase.WndProc(Message& m)

        at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message&
     m)

        at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg,
     IntPtr wparam, IntPtr lparam)


Back to Top
kimj View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 1391
Post Options Post Options   Quote kimj Quote  Post ReplyReply Direct Link To This Post Posted: 19-May-2011 at 3:49pm
It's unfortunate that you can't change the data model, since a non-key property like "SortOrder" would certainly make things easier.
 
First, DF Classic does actually support "re-using" a primary key; so you can delete the old entity and then create a new entity with the key info from the old entity, all within a single SaveChanges transaction.  This might make the logic a bit easier.
 
To get the swapping working, you'll need to call BeginCheckpoint() before every pair swap.  This does add extra overhead, but will avoid the unique constraint problem, which as you guessed occurs because the checkpoint logic contains its own Dataset to hold changes and doesn't handle PrimaryKey changes well.
 
 
Back to Top
aschaff View Drop Down
Newbie
Newbie
Avatar

Joined: 28-May-2009
Posts: 13
Post Options Post Options   Quote aschaff Quote  Post ReplyReply Direct Link To This Post Posted: 19-May-2011 at 4:04pm
I tried using BeginCheckpoint before each pair swap, and the move up/down work now but if I hit cancel after making some swaps I get this error from RollbackCheckpoints:
"SystemArgumentException: This row already belongs to this table."

-AJ
Back to Top
kimj View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 1391
Post Options Post Options   Quote kimj Quote  Post ReplyReply Direct Link To This Post Posted: 19-May-2011 at 7:43pm
That's unfortunate.  I can't find a solution that will handle both save and rollback without an error when either PK swapping or delete/create is used.
 
I'd suggest not modifying the primary keys "in flight".  Instead track the new sort order of the parameter - either via a synthetic property on the entity or some companion structure.  When "OK" is hit you can then do the "final" fixup, and if "Cancel" a rollback is either not needed or will not involve the primary keys.
 
Back to Top
aschaff View Drop Down
Newbie
Newbie
Avatar

Joined: 28-May-2009
Posts: 13
Post Options Post Options   Quote aschaff Quote  Post ReplyReply Direct Link To This Post Posted: 20-May-2011 at 6:16am
Thanks for the advice. I knew this would be a tough one. I'm actually looking into changing the primary key. I said I can't change the data model but that was imprecise. What I can't do is break or modify another existing app that also maintains this data. So changing the table's key to an identity column might work since it is self inserting. But I ran into a DBCC permission issue there. I posted it in a separate thread if you can take a look. Solving that might get me past this indirectly.

Thanks again for your help.
-AJ
Back to Top
smi-mark View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 24-Feb-2009
Location: Dallas, Texas
Posts: 343
Post Options Post Options   Quote smi-mark Quote  Post ReplyReply Direct Link To This Post Posted: 20-May-2011 at 9:30am
Hi AJ,

One thing I have done in the past when altering the database isn't feasible:

Add a property to your class called SortOrder, which is initially set to the primary key, when the users move things around it is only altering this property that isn't persisted. Would that work?

I can post an example if that would make more sense.

Mark
Back to Top
aschaff View Drop Down
Newbie
Newbie
Avatar

Joined: 28-May-2009
Posts: 13
Post Options Post Options   Quote aschaff Quote  Post ReplyReply Direct Link To This Post Posted: 20-May-2011 at 11:33am
Thanks for the idea. I thought of it, too, just decided that it would add a bunch of code that I didn't want to look at down the line. It seems clear to me that the column really shouldn't be part of the key, so I'm leaning towards adding an Identity column to the database and making that the primary key. The old program will still work, blissfully unaware that the PK changed. Also, I won't have to play my +/- 100 game when saving to the DB either, so the code shrinks even more. I just have to avoid the DBCC issue with identity columns, which I have the info on how to do now...just have to go try it.

Thanks for your help, both of you!
Back to Top
aschaff View Drop Down
Newbie
Newbie
Avatar

Joined: 28-May-2009
Posts: 13
Post Options Post Options   Quote aschaff Quote  Post ReplyReply Direct Link To This Post Posted: 20-May-2011 at 12:03pm
Ouch, identity column idea didn't work. I guess I'm going to have to play the SortOrder game after all.

Mark, if you have an example, I'm interested. :)

-AJ
Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down