Child Objects remaining Dirty after Update/SaveChild Objects remaining Dirty after Update/Save
Old forum URL: forums.lhotka.net/forums/t/5320.aspx
Galen posted on Friday, August 29, 2008
Hello,
This is my first time posting but I have utilized the information on these forums for a few years now and would firstly like to thank everyone for the contributions.
I've recently started using CSLA 3.6.0 for a hobby project. I haven't used CSLA since version 2 and i'm having problem with a child collection not setting the IsDirty=false to each of it's items during an update.
A summary of my structure is as so:
//Root Class
public class Property : BusinessBase<Property>
//Root Class - Property
private static PropertyInfo<Collections.PropertyFlorae> PlantsProperty = RegisterProperty(new PropertyInfo<Collections.PropertyFlorae>("Plants"));
public Collections.PropertyFlorae Plants
{
get
{
if (!(FieldManager.FieldExists(PlantsProperty)))
{
LoadProperty<Collections.PropertyFlorae>(PlantsProperty, Collections.PropertyFlorae.NewPropertyFlorae());
}
return GetProperty<Collections.PropertyFlorae>(PlantsProperty);
}
}
//Root Class -- DataPortal Update Method
[Transactional(TransactionalTypes.TransactionScope)]
protected override void DataPortal_Update()
{
using (var ctx = ContextManager<NativeScape.Data.NativeScapeDataContext>.GetManager(NativeScape.Data.Database.NativeScape))
{
if (IsSelfDirty)
{
// insert project data
ctx.DataContext.updateProperty(
ReadProperty<int>(IdProperty),
ReadProperty<string>(TitleProperty),
ReadProperty<int>(ProfileIdPRoperty),
ReadProperty<SmartDate>(CreatedProperty));
}
// update child objects
//DataPortal.UpdateChild(ReadProperty<Collections.PropertyFlorae>(PlantsProperty), this);
FieldManager.UpdateChildren(this);
}
}
//---------Child Collection Class
//From my research there is no need to implement any DataPortal methods for //this class
public class PropertyFlorae : BusinessListBase<PropertyFlorae, PropertyFlora>
//public method to add an item to the collection
public void Assign(int floraid, int quantity)
{
if (!(Contains(floraid)))
{
PropertyFlora propflr = PropertyFlora.NewPropertyFlora(floraid, quantity);
this.Add(propflr);
}
else
{
throw new InvalidOperationException("Flora already assigned to property");
}
}
//---------Child Items
public class PropertyFlora : BusinessBase<PropertyFlora>
// Child Item -- DataPortal Update Method
private void Child_Update(NativeScape.Library.BusinessObjects.Property data)
{
using (var ctx = ContextManager<NativeScape.Data.NativeScapeDataContext>.GetManager(NativeScape.Data.Database.NativeScape))
{
ctx.DataContext.updatePropertyFlora(
ReadProperty<int>(IdProperty),
ReadProperty<int>(FloraIdProperty),
(int?)data.Id,
ReadProperty<int>(QuantityProperty));
}
}
// In my web user control this is how it's all called:
//CurrentProperty is just a static method that reads from an instance of the
//property class stored in the session
NSProperty.CurrentProperty.Plants.Assign(floraid, qty);
NSProperty.CurrentProperty.Save();
Ok so as you guys can see, the code is similiar to what's used in the ProjectTracker sample project. And it *does* work, in the sense that the updates all go through to the database fine.
What doesnt seem to be working is each time a child item is inserted into the collection and then Updated, the item remains dirty.
Consequently the next time the Save is called the item is inserted into the database again.
From my debugging and viewing the persisted instance after a Save has occured. The IsDirty properties most definately remain true after the update has occurred.
any help would be greatly appreciated.
galen
Galen replied on Sunday, August 31, 2008
*bump*
Still having this issue.
Thanks in advance
Galen
RockfordLhotka replied on Sunday, August 31, 2008
The most common reason for this issue (and it is pretty common) is that you are continuing to use the old object after a save. In other words, people often do this:
obj.Save();
Which is wrong. You MUST do this:
obj = obj.Save();
This is discussed in the books, and starting in version 3.5 is mandatory even when using the local data portal configuration (though you can get the older (but technically broken) behavior through a config setting - this is discussed in Using CSLA .NET 3.0).
Galen replied on Sunday, August 31, 2008
Thanks Rocky,
I do
own the book... but obviously missed this simple caveat.
Regardless it seems a little puzzling to me that an object Save must be called this way?
I can understand the benefits of having a return object, but having the method not modify the actual instance seems non intuitive.
Was the primary reason for this to give the developer an option of keeping the 'Unsaved' instance?
Thanks Again
Galen
RockfordLhotka replied on Monday, September 01, 2008
The reason Save() returns another instance is because the .NET
serialization process creates a clone of the object across the network. This is
discussed in Chapter 1. Because the object graph is cloned from client to app
server, and from app server to client, the client ends up with a different
object as a result.
When using the local data portal CSLA used to just modify the
existing object instance (no cloning). The problem there is a big one: if your
update fails part way through the process, the database will roll back and be
consistent. But the object graph will be very inconsistent. Some objects
will have new timestamps or new database-generated id values, etc. None of
which will be correct. So the object graph (in that case) is totally broken.
That meant that the UI developer had to write code to determine
if the data portal would be local or remote, and to work differently in those
two scenarios. It also meant that the UI developer had to clone the object
graph before saving when using a local data portal. Not doing that clone was a
bug.
So in CSLA 3.0 I added the option to have CSLA do the clone for
you (because too many people were forgetting, and that is a bug). And in 3.5 I
switched the default so CSLA defaults to doing the right thing, though you can use
the config setting to revert to the older, broken, behavior for backward
compatibility.
Rocky
Copyright (c) Marimer LLC