Vibrant discussion about CSLA .NET and using the framework to build great business applications.
I have an object graph that contains a parent, children, and grandchildren.
We are using Silverlight as the client, and we have a screen that supports editing the entire graph at the same time. So invoking Save() on the parent will save all of its grand/children.
The specific scenario that we are plagued with occurs when a user deletes a child or grandchild, and that delete fails on the server for whatever reason (typically due to concurrency, etc.)
Typically, this scenario leaves the entire object in an invalid state, as the Save() will always fail due to that deleted object failing on Child_DeleteSelf().
This can be very frustrating for a user, as they may have made changes to the parent and children as well, and this scenario basically forces them to reload the entire graph.
So my question is: is there a way to undelete the item on the server so that I can get the object graph back into a valid state? I've tried this in ways, and always get an exception about the collection changing. Is there a sneaky way around this? Admittedly, I have not dug real deep into this.
Depending on the feedback that I get, I'll follow up with further questions.
You must implement your own Child_Update method on the list:
This is the default implementation in BusinessListBase:
protected virtual void Child_Update(params object parameters)
var oldRLCE = this.RaiseListChangedEvents;
this.RaiseListChangedEvents = false;
foreach (var child in DeletedList)
foreach (var child in this)
if (child.IsDirty) DataPortal.UpdateChild(child, parameters);
this.RaiseListChangedEvents = oldRLCE;
. You need to override this method and implement your own to "re-add" the item if delete failed.
Jonny Bekkum, Norway
No, you cannot do this within Child_DeleteSelf.
You can however do this within the the update method but that also means that you cannot use the helper methods in CSLA like DataPortal.UpdateChildren.
Altho' I am unsure as to how the user dialog would be if this is partially successful save....
"You can however do this within the the update method"
Which update method are you speaking of? The DP_Update() of the root object?
I see, that would work, but not the most ideal.
What if I allowed the delete to complete (catch the exception and leave the item removed from the collection) but then want to notify the user that the delete was unsuccessful and will reappear when the object is reloaded? Not ideal, but this way all the DP methods would succeed.
I thought about adding a ConcurrencyError property to the deleted object, but of course, the object won't exist after the object returns to the UI.
I thought about traversing upwards until I grab it's parent and adding the message there, but that's pretty hacky.
I thought about writing it out to the ClientContext or GlobalContext, but it seems that values written to them on the server are not persisted back to the client (only the originating values that came from the client are).
Any thoughts on how I could marshal messages back with the payload?
GlobalContext is 2-way so that may be used but I would prefer an separate AppContextObject and add the messages to the root object as it´s own property. It is a bit tricky when you do async DataPortal calls so I would prefer to make it a property on the root object.
You can override the DataPortal_BeginInvoke and DataPortal_EndInvoke on the root object and set the property value in EndInvoke from a "context" variable.
Yes, this is actually the route that I have chosen. Not happy with it, but I essentially add concurrency error messages to the GlobalContext, and on DataPortal_OnDataPortalInvokeComplete (which only invokes for the root object), take all of the messages and add them to a ConcurrenyErrors list (defined in my own base class that inherits from businessBase). Still a bit hacky, but it works.
The problem I have now is with Root business lists, as they don't honor serializing properties.
I know I'm supposed to override the OnGet/Set methods, but they don't seem to be honoring serializing my property back to the client.
I have defined a property of type MobileList<string> to hold all of the concurrency errors, and then de/serializing via the OnGet/Set/Children methods, but to no avail.
Any thoughts on that?
Yes, you cannot use all types of root objects with this technique.
I would change the root list objects to be child objects of an EditableRoot object.
Have you thought of maybe using a CommandObject to wrap the save method (somewhat like the CQRS type of code)?
That's a great idea, thanks!