Undeleting Root Object

Undeleting Root Object

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


amselem posted on Saturday, January 26, 2008

Maybe this is a dumb observation, but I think that there should be an Undelete method that sets mIsDeleted = False for EditableRoot BO's

If I have a "Delete" button in my UI that calls Delete, but fails when saving:
MyBO.Delete()   ' Mark the BO as deleted
MyBO.Save()    ' Save fails and throws an exception (for ex: Server unavailable)

Then the server is back again but the user changes its mind, updates the BO and press the "Save" button... the BO gets deleted instead of updated! (because is still marked for deferred deletion). Ideally I'd like to do this:

Try
   MyBO.Delete()
   MyBO.Save()
Catch ex As Exception
  MyBO.Undelete()
End Try
  
The Undelete sub is very easy to implement but I think it should be included in the Framework. What do you think ?

Regards
Jacobo

ajj3085 replied on Monday, January 28, 2008

The problem is that the object is left in an invalid state.  What you should be doing is cloning the object first, and then saving the clone object.  In the newest version of the framework, thre is a flag that does this automatically for you.

amselem replied on Monday, January 28, 2008

The automatic cloning of the framework doesn't solve the problem because it would clone the object that is already marked for deletion, so the original one has IsDeleted = True.

If I clone it manually before calling .Delete() then I would incur in the overhead of two clonings wich is undesirable. I would have to turn off automatic cloning wich is undesirable too.

 

 

tetranz replied on Monday, January 28, 2008

Maybe I'm missing something but doesn't CancelEdit() do what you want?

BeginEdit()
Delete()
Clone and Save()
If Save() fails then you should still have the object before cloning. You can then do a CancelEdit() which is effectively an Undelete.

amselem replied on Monday, January 28, 2008

No, BeginEdit/CancelEdit is the usual with Child objects but the framework disallows saving of Root objects with EditLevel > 0, see this snippet from BusinessBase.Save :

If EditLevel > 0 Then
    
Throw New Validation.ValidationException( _
         My.Resources.NoSaveEditingException)
End If

So with your suggested approach, BeginEdit would set EditLevel = 1, the clone would have EditLevel = 1 too and Save would fail with NoSaveEditingException

ajj3085 replied on Monday, January 28, 2008

Clone, then call ApplyEdit.  You can then call CancelEdit on your original object if the delete fails.

More to the point though, why are you allowing a user to continue editing an object when its save operation failed?  That seems to be a very odd line of thinking... "well I couldn't delete the object, so I guess I'll keep working with it?"

amselem replied on Monday, January 28, 2008

No, as I said before I'd like to use the "automatic cloning on save" feature provided with the framework so if I clone the object manually as you suggests I would incur on two clonings (the fw would clone the object again) wich is undesirable. This setting is global and I'd prefer to not turn it off.

As for the use case, for ex, think on an user who try to delete an object but hasn't enough permissions so he gets an error. After seeing the problem, he wants to update a status field to "disabled" or at least write in a memo field "this must be deleted by admin". But pressing "save" throws the same "not enough permission to delete" error again and again because the BO still has IsDeleted = True.

My thinking is that if the framework provides a method to allow the BO user to mark an Editable root object as deleted, it should provide a method to clear this flag if the BO user repents of this action.

Thanks for discussing this!
Regards

ajj3085 replied on Monday, January 28, 2008

amselem:
No, as I said before I'd like to use the "automatic cloning on save" feature provided with the framework so if I clone the object manually as you suggests I would incur on two clonings (the fw would clone the object again) wich is undesirable. This setting is global and I'd prefer to not turn it off.


Well, I think that's the best way to proceed, even if you have two clones.  Your database access will take more time than any cloning, so unless your object graph is pretty large your user shouldn't notice any slowdown.

amselem:
As for the use case, for ex, think on an user who try to delete an object but hasn't enough permissions so he gets an error.

Your UI should be preventing this by checking CanDelete, or maybe even CanExecuteMethod (if you need per instance authorization).

amselem:
After seeing the problem, he wants to update a status field to "disabled" or at least write in a memo field "this must be deleted by admin". But pressing "save" throws the same "not enough permission to delete" error again and again because the BO still has IsDeleted = True.

Again, if your UI prevents the user from marking delete (because your BO should be able to answer that question) then this won't be an issue.  Also, the cloning before calling ApplyEdit will fix this issue as well, but you shouldn't need it if your UI correctly prevents Delete from being called.

amselem:
My thinking is that if the framework provides a method to allow the BO user to mark an Editable root object as deleted, it should provide a method to clear this flag if the BO user repents of this action.

Well, it does, just not after the user has also indicated they are done editing the object and attempts to save it.  What you're asking is to be able to continue editing an object which is now in an invalid state.  I don't think that's a good idea.

amselem replied on Monday, January 28, 2008

Thanks for your comments ajj3085,

ajj3085:
Well, I think that's the best way to proceed, even if you have two clones. 

Yes currently I'm using the two cloning approach and it performs well in my use case.

All this thinking came because it seemed to me a little weird to clone the object before saving when csla is doing it automatically for me. So that thinking lead me to determine that the Business Object user should manage IsDeleted as any other state variable of the BO. In fact I usually create rules involving IsDeleted (for disabling the rule when deleting), so changing the IsDeleted value as many times as the BO User wants would never leave the BO in an invalid state.

ajj3085:

Your database access will take more time than any cloning, so unless your object graph is pretty large your user shouldn't notice any slowdown.

Well, maybe my examples weren't the best for showing what I wanted, think that Save can throws an exception withouth touching the database, for ex. if the BO has broken rules and is not valid, save would fail.

ajj3085:
Your UI should be preventing this by checking CanDelete, or maybe even CanExecuteMethod (if you need per instance authorization).

ajj3085:
Again, if your UI prevents the user from marking delete (because your BO should be able to answer that question) then this won't be an issue.  Also, the cloning before calling ApplyEdit will fix this issue as well, but you shouldn't need it if your UI correctly prevents Delete from being called.

When I talk about "BO user" I mean "UI programmer" not "application end-user". So maybe is under my control that he can guide the end-user using UI elements (like enabling/disabling the save button) or maybe not, maybe he forgot to implement it or maybe there wasn't time to do it. Me, as a BO developer, I don't like to rely on the UI and I prefer to do checkings and throw exceptions where has sense.

ajj3085:
Well, it does, just not after the user has also indicated they are done editing the object and attempts to save it.  What you're asking is to be able to continue editing an object which is now in an invalid state.  I don't think that's a good idea.



As I said, modifying the IsDeleted value don't leave the BO in an invalid state. Maybe the BO user simply wants to revert the value before saving. I know that the cloning approach fixes it, it's just only that I didnt find why Delete() is one-way only, it seems very simple to create an Undelete sub.

JoeFallon1 replied on Tuesday, January 29, 2008

"it seems very simple to create an Undelete sub. "

It seems like you have given this a lot of thought.

So why not just create the Undelete Sub in your Base class that inherits from CSLA and which all your BOs inherit from?

Or perhaps you did already and are suggesting this so Rocky can consider adding it to the framework?

Joe

 

amselem replied on Tuesday, January 29, 2008

Well I couldn't create the sub in my Base class because mIsDeleted is private, and there isn't anything like MarkUndeleted. I'm currently solving this issue cloning manually the BO before saving (in addition to the csla "clone before save" feature).

I was not suggesting to Rocky to add the sub, it's just that when I found this issue I went to see if there was anything supporting the undeletion of root objects, but I didn't find anyhing in the framework nor the forum. So I didn't know if my approach was incorrect or there is a good reason for not undeleting EROs.

Of course I'd be glad if Rocky consider this sub is worth enough to add it to the framework.

 

amselem replied on Thursday, January 31, 2008

Rocky , any wise words on this one ?

ajj3085 replied on Friday, February 01, 2008

I would like to hear Rocky's thoughts on this as well.

If you really feel you need it though, you can modify the Csla framework source code yourself.  I've done this when needed, which is one of the reasons I keep the source under version control, and let my build system also build Csla. 

If you want to add it, I recommend making BusinessBase a partical class.  Then create another code file, BusinessBase.Extensions.cs (or some such thing) and put you Undelete code in there.  That way, your modified code is in a competely seperate file.  Upgrading is a a matter of overwriting Csla code with the newest download, compiling and seeing where the compiler complains about missing partial modifiers and adding them back in.

HTH
andy

amselem replied on Friday, February 01, 2008

Hi Andy,

I like a lot your idea of making CSLA's classes as partial classes. I also have my source under version control but I was changing the framework code when needed a hack or something like this. Then on each upgrade I was using winmerge to keep my changes, a manual and error prone task. Thanks for your comments!

Copyright (c) Marimer LLC