EditableRootListBase with DataGridView doesn't handle sql update errors gracefully

EditableRootListBase with DataGridView doesn't handle sql update errors gracefully

Old forum URL: forums.lhotka.net/forums/t/3274.aspx


AzStan posted on Thursday, July 26, 2007

Here's a problem that has plagued me for a while now since the introduction of CSLA 2.1. 

When using object derived from the EditableRootListBase with the DataGridView, I get a undesirable behavior when a sql server error is thrown.  This typically happens when a user edits a row, and clicks to the next row, triggering an update of the dirty business object.  If the 'Update' stored procedure then throws an error, the error will be passed to the grid, and can be handled in the DataError event of the DataGridView.  I typically give the user an error message. 

The problem is, that after the error is handled, the focus has shifted to the new grid row, and the edited row still shows the offending edits as if they were successful.  Only if the grid is closed and reopened, will the user discover that the edits failed.

I have tried forcing a refresh of the binding source: e.g. ...BindingSource.ResetBindings(false); in the DataError event handler. I've also tried canceling the event.  In all cases, the focus moves as if the Save was successful, and there is nothing other than the error message to indicate that the edits were not committed.

BTW:  Normally, the business object validation would prevent the save from initiating.  There are certain special cases, however, (concurrency, aggregate validation) where I have given up on client side validation and simply let the SQL server tell me if the record can be saved.

I have tested this on CSLA V3.0.1 and the problem is still present.

Thank you to anyone who has a workaround, or can shed light on the error of my ways.

RockfordLhotka replied on Thursday, July 26, 2007

I'm not entirely sure what you want to happen.

There's no way to roll the object back to a previous state, because when the user moves off the row the changes are committed in memory within the object.

Perhaps, and this would take some testing, the SaveItem() method in ERLB could be changed to do something like this:

  1. Clone the item
  2. Process the item as it does now
  3. In case of exception, set this[index] to the clone
  4. Call CancelEdit() and BeginEdit() on the clone, thus undoing whatever bad stuff the user did

I'm not sure that's a great user experience though, because any changes they made would just disappear, but maybe that's better than today?

In any case, there's no way for the object to control how the grid manages its focus. If you want focus to switch back to the previous row, you'll need to do that at the UI layer - probably in your DataError event handler.

lotrij replied on Tuesday, July 31, 2007

Rocky,

Thank you for the response to my original post:  http://forums.lhotka.net/forums/thread/16553.aspx

Sorry for not posting here first.

IEditableObject's EndEdit causes an AcceptChanges to be called before ERLB's SaveItem is reached (at least in the flow that I experienced).  So, I don't think that a change in ERLB's SaveItem will work, since by the time ERLB's SaveItem is called, the object's edit level is already 0, so the object doesn't have its original state.

Do controls expect the edit level to be 0 after an EndEdit, even if an error occurs?  If so, then I guess it's not an option to try to find a way to keep the original state on the undo stack.  If not, then maybe EndEdit can be changed in such a way that the original state will be pushed back on the stack if an exception occurs.

In the absolute worst case I suppose that we could disable IEditableObject and manually control the edit level and saving based on grid or binding source events, but this would be an ugly option.

RockfordLhotka replied on Tuesday, July 31, 2007

lotrij:

Do controls expect the edit level to be 0 after an EndEdit, even if an error occurs?  If so, then I guess it's not an option to try to find a way to keep the original state on the undo stack.

Let's be clear here - I'm doing something with EditableRootListBase that is not a "supported scenario" for data binding. This isn't a model that's really been available since VB3 went out of style in 1995, because ERLB basically replicates the DynaSet concept.

So data binding is essentially calling EndEdit() on the current row because the user moved off or did something to commit the change. Data binding expects to call BeginEdit() when the user arrives on the row they are moving to, but the exception stops that processing in the middle.

What ERLB is doing, is jumping in the middle by detecting the EndEdit() call (AcceptChanges() really) and using that as a trigger to know to save that child object now that the user has attempted to move to some other row.

The expectation is that data binding will then call BeginEdit() on some other child, starting the cycle again.

So yes, by the time ERLB is involved the edit level is 0, because the user "committed" the change.

I am still not convinced that resetting the row to a previous state would be good anyway. The user experience could be quite jarring - to have their changes just disappear out from under them. Which is basically what would happen if the object could somehow jump back to a previous state.

On the other hand, there's no doubt that the current behavior is confusing to the user as well...

Copyright (c) Marimer LLC