Child Objects remaining Dirty after Update/Save

Child 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