Why clone on update?

Why clone on update?

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


cmay2 posted on Wednesday, November 11, 2009

The last project I used with CSLA was v2.0. Now I'm trying to work on something using v3.7.1 and I'm not sure if what I am seeing is:
1) something new
2) something I'm doing wrong
3) both not-new and as-expected

So, in my last project using CSLA 2.0, I would create and save objects like this:

Dim o as MyObj = MyObj.NewMyObj()
' o.Id is 0 at this point
o.SomeProp = 123
o.Save()
' o.Id is now 10 (or whatever Identity from the DB)

In the DataPortal_Update I would do the insert to the DB and get back the Identity value of the object, and assign it to the local variable.

Now, when I do that same thing in CSLA 3.7 the object "o" doesn't have it's ID value set.

In debugging I see that:
dim temp as MyObj
temp = o.Save()
' temp.Id = 10 o.Id = 0

Setting CslaAutoCloneOnUpdate = false in the web.config causes it to not clone, and act as I had expected it to.

I am not using anything remote with my objects if that matters.

So my questions are:
1) Is it a bad practice to have CslaAutoCloneOnUpdate = false?
2) Why is the default behavior to Update (and return) a cloned object, instead of the object you are calling .Save() on?
3) Should all .Save() operations look something like:
dim temp as MyObj
temp = o.Save()
o = temp

or even just
o = o.Save()
4) If I use CslaAutoCloneOnUpdate=false, and do my updates just by calling o.Save() and not bothering with catching the returned object, am I setting myself up for disaster if I end up moving to a remote server in the future?

Sorry for loading up this post, I'm just trying to understand what is going on here. Thanks if anyone can help me.

RockfordLhotka replied on Wednesday, November 11, 2009

This has been discussed on the forum a few times in the past.

If you don't like the behavior, you can turn it off - reverting to the old (broken) behavior.

The reason the old behavior was broken, is that it relied on the UI developer to do the clone themselves, to properly handle the case that an exception occurred during the save. Of course almost nobody write correct UI code (yours is a good example).

The problem is that a save operation alters the state of the object graph. The metastate is altered (IsDirty, IsNew, etc) and often the database generates new primary keys and timestamps. If the save fails half-way through, the database rolls back due to your use of transactions. But the object graph is left in an indeterminate and broken state.

So the UI code has to wrap all Save operations in a try..catch and either do a clone before the save, or re-fetch the entire object graph in the case of an exception.

Since virtually nobody was doing the right thing, it was a source of constant pain for people using the framework. Additionally, when I implemented ERLB, CslaDataProvider and other objects that do saves for you, I had to constantly replicate the correct behavior - which was just wrong.

So I fixed the problem at the source - in the data portal - so it does the right thing.

But if you want the broken behavior, you can revert to it. I just recommend wrapping all your save calls in try..catch blocks so at least your app isn't broken.

On the other hand, if you do leave the autoclone enabled (which I recommend), then your typical save code should be like this:

o = o.Save();

and even that should be in a try..catch block, because you can still get exceptions of course, and you should report them to the user appropriately.

Copyright (c) Marimer LLC