CSLA .NET

Vibrant discussion about CSLA .NET and using the framework to build great business applications.

CSLA 3.5 Bug - Child Collection Objects Not Updating Correctly After Save.

rated by 0 users
This post has 15 Replies | 1 Follower

Not Ranked
Posts 4
tabaguley Posted: Sat, Jun 28 2008 1:03 PM

I have come across what I believe to be a bug with managed collection properties in child/grand-child collection objects.  I am using CLSA 3.5.0, however, I have updated most of the code for the FieldManager/n-level undo stuff to the latest (3.5.1) posting on SVN.

Here is the basic structure I'm working with:

HardwareDevices (Editable Root List)

   -> HardwareDevice (Editable Child Object)

         -> DeviceProperties (Editable Child Collection)

               -> DeviceProperty (Editable Child Object)

Here is the problem:

When a new DeviceProperty is added to the DeviceProperties collection of a HardwareDevice, everything works normally until HardwareDevices is saved.  After saving, any DeviceProperty objects added in will show up in the DeviceProperties collection; however, when Save is again called on HardwareDevices, none of the HardwareDevice objects report back as being Dirty (and hence, none of the DeviceProperty objects added after the first Save get saved).

I have tried using the CSLA 3.5 Managed Property setup with and without a state variable for DeviceProperties, and even tried the pre-3.5 method of using just a state variable and overriding IsDirty and IsValid.  Each manner of doing things yielded the same results. 

From what I can tell, the internal FieldManager isn't tracking reference-types correctly.  I noticed that SmartDate was changed from a Class(reference-type) to a Structure (value-type).  When upgrading several of my custom validation objects, I also had to convert them to Structures in order to get them to work correctly with the Managed Properties feature of CSLA 3.5.  I don't know if this could be part of the problem or not, but I thought it worth mentioning.

Top 10 Contributor
Posts 9,270

Are you in a Windows Forms environment? If so, are you unbinding and rebinding the objects to the UI correctly?

It sounds like your UI may still be bound to the old object instance, not the new one returned by the Save() method.

It isn't an object vs struct thing - SmartDate has been a struct for a few years now.

But if you recently switched from 3.0 to 3.5, and you are using a local data portal, you may have missed the fact that the local data portal now acts like a remote data portal in terms of returning an updated object instance, rather than altering your instance in-place. This is discussed in the change log and can be reverted to the old (bad) behavior using the AutoCloneOnUpdate config setting.

Rocky

Not Ranked
Posts 4
tabaguley replied on Mon, Jul 7 2008 11:36 AM

I am actually upgrading from CSLA 1.1 to 3.5 (finally!), and am finding I have a lot to learn about the new framework.  Yes, I am working in a Windows Forms environment.  With that said, I believe I've figured out the problem. 

It appears that CSLA objects are designed to work (best) with a BindingSource object, which I do not use.  I have played around with the BindingSource object, and find it problematic - it seems to be just an extra ("middle-man") object that isn't necessary the way I do things.  Because I do not use a BindingSource, I believe that child collections contained within a given object do not get initialized properly. 

I do understand how the local data portal works, and I do everything correctly as far as binding, unbinding, and updating references.  However, even though I update the references, the child collection objects end up as Nothing after a Save operation. 

But I have come up with a fix: prior to save, I simply call ToString on each Child Collection in each object (I also override ToString on each Child Collection to simply return the Name & Count of the collection).  This appears to initialize the collection within the FieldManager (which I believe is what te BindingSource object would be doing if I used one), and then everything works fine after the Save. Here's an example:

Public Overrides Function Save() As ParentCollection
    'See if Save is allowed...
    If Not CanEditObject() Then
        Throw New Exception("User not authorized to save Parent Collection.")
    End If

    'Ensure Child Collections Are Initialized...
    For Each child As ChildObject In Me
        child.GrandChildCollection.ToString()

        For Each grandChild As GrandChildObject In child.GrandChildCollection
            grandChild.GreatGrandChildCollection.ToString()
        Next
    Next

    Return MyBase.Save()
End Function

 

Top 10 Contributor
Posts 9,270

It is true that starting with .NET 2.0 I only support the use of a BindingSource for data binding. Microsoft used the BindingSource to fix a bunch of bugs and issues around data binding, and I didn’t want to suffer with those 1.x issues myself J

 

If you’ve already got good workarounds for all the .NET 1.x binding issues (and it sounds like you do) that’s good – but it doesn’t surprise me that you would have to do some work in or around CSLA to accommodate the older binding scheme.

 

Rocky

 

Rocky

Not Ranked
Posts 9
van replied on Fri, Nov 7 2008 2:35 AM

I have a problem with saving collection which associated to object.

If I add new item to collection, collection is dirty but object is not, and DP_Update never invoked.

Before save I must do folowing:

Sednica.DokumentList = null;

Sednica.DokumentList = documentList;

Sednica = Sednica.Save();

documentList is same object as a Sednica.DokumentList

Top 10 Contributor
Posts 9,270

If the object isn’t dirty it won’t be saved. You need to ensure that the newly added object is dirty.

 

Rocky

 

 

From: van [mailto:cslanet@lhotka.net]
Sent: Friday, November 07, 2008 7:07 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: CSLA 3.5 Bug - Child Collection Objects Not Updating Correctly After Save.

 

I have a problem with saving collection which associated to object.

If I add new item to collection, collection is dirty but object is not, and DP_Update never invoked.



Rocky

Not Ranked
Posts 9
van replied on Thu, Nov 13 2008 5:06 AM

I think you didn't understand me. I have class Sednica, inherited from BaseObject<T> which inherited from BusinessBase<T>. In Sednica I have PrisutnostList. When I load data from database PrisutnostList is not dirty and Sednica is not dirty. That is OK. If I add new item in the PrisutnostList or change some item\items, list is dirty and that is OK. But Sednica is not dirty, and I can't save changed list because I save list in DP_Insert or DP_Update in the Sednica object under transaction. PrisutnostList is not child object. I try with child object and same problem appear.

I try subscribe to event ListChanged and you can see that code under comment. But this is not work because ListChanged event fires several times and I get StackOverflowException.

public class Sednica : BaseObject<Sednica>

{

   private static PropertyInfo<PrisutnostList> PrisutnostListProperty = RegisterProperty<PrisutnostList>(typeof(Sednica), new PropertyInfo<PrisutnostList>("PrisutnostList", "PrisutnostList"));

   private PrisutnostList _prisutnostList;

   public PrisutnostList PrisutnostList

   {

   get

   {

      if (_prisutnostList == null)

      {

      _prisutnostList = PrisutnostList.GetPrisutnostBy(this);

      //if (_prisutnostList != null)

      //{

      // _prisutnostList.ListChanged += _prisutnostList_ListChanged;

      //}

     }

      return GetProperty<PrisutnostList>(PrisutnostListProperty, _prisutnostList);

   }

   set

   {

         SetProperty<PrisutnostList>(PrisutnostListProperty, ref _prisutnostList, value);

         //_prisutnostList.ListChanged += _prisutnostList_ListChanged;

     }

   }

   //void _prisutnostList_ListChanged(object sender, System.ComponentModel.ListChangedEventArgs e)

   //{

         // MarkDirty();

   //}

}

Top 10 Contributor
Posts 3,922
Andy replied on Thu, Nov 13 2008 7:51 AM
 Hmm... you're using private backing fields.  I don't think that will allow your root class to automatically update IsDirty / IsValid.  I suspect you'll need to override like this:

public override bool IsDirty {
   get { return IsSelfDirty || _prisutnostList.IsDirty; }
}

public override bool IsValid {
   get { return base.IsValid && _prisutnostList.IsValid; }
}

Top 10 Contributor
Posts 9,270

Yes, if you use private backing fields for your child objects you MUST use the old style of coding, because CSLA can’t do the work for you.

 

I strongly recommend using managed backing fields for child objects, even if you use private backing fields for primitive types, because this allows CSLA to automatically do all the little, easy-to-forget things you have to do to make a child object work properly.

 

Rocky

 

Rocky

Top 75 Contributor
Posts 113
AaronH replied on Thu, Nov 13 2008 12:58 PM

I think I've run into the same issue, but I'm not satisfied with the work-around (it's terribly kludgey).

Please try running the following code in PTracker.  Note that I have made no changes to any of the 3.5.2 PTracker classes.

Project p = Project.NewProject();
p.Name = "Aarons project";
p.Started = DateTime.Now.ToString();
p.Ended = DateTime.Now.AddYears(2).ToString();
//System.Diagnostics.Debug.WriteLine("Resources = null? : " + (p.Resources == null).ToString());
p = p.Save();
p.Resources.Assign(2);
p.Save();

It should throw an exception stating that Resources is null. If you uncomment the Debug.WriteLine statement and run again, it works fine.  I'm confused.

Top 10 Contributor
Posts 3,922
Andy replied on Thu, Nov 13 2008 1:12 PM
Sorry, I don't follow.  Prior to managed fields, you HAD to override IsDirty / IsValid for Csla to work.  Using managed fields allows you to not have to implement that code.  Neither are kludgy though.. that's how you use Csla.

I checked out the 3.6 code, and don't see where it would thrown an exception.  On what line is the exception thrown?
Top 75 Contributor
Posts 113
AaronH replied on Thu, Nov 13 2008 2:13 PM

Please run the code and you'll see what I'm talking about.  When you hit the line that attempts to assign a resource to the project, the resources collection is NULL.  If you uncomment the Debug statement, the resources collection will NOT be NULL when it comes time to assigning the resource to the project.  Does that make sense?  Again, running the code will make everything clear.

Thanks!

Top 10 Contributor
Posts 3,922
Andy replied on Thu, Nov 13 2008 2:43 PM
Ok, I ran the code.  My confusion lies in that the bug you're encountering has nothing to do with the solution to the other poster's problem.

I would upgrade to 3.6, because if you run your same code using Csla 3.6, it functions fine.
Top 75 Contributor
Posts 113
AaronH replied on Thu, Nov 13 2008 3:47 PM

Yeah, I should have been more clear as to which post I was referencing.  Here is the quote that I was referring to:

-----------------------------

tabaguley

I do understand how the local data portal works, and I do everything correctly as far as binding, unbinding, and updating references.  However, even though I update the references, the child collection objects end up as Nothing after a Save operation.

But I have come up with a fix: prior to save, I simply call ToString on each Child Collection in each object (I also override ToString on each Child Collection to simply return the Name & Count of the collection).  This appears to initialize the collection within the FieldManager (which I believe is what te BindingSource object would be doing if I used one), and then everything works fine after the Save. Here's an example:
 
------------------------------

Now, no offense to the original author, but if that's not a kludgey workaround, I don't know what is ;)

Not Ranked
Posts 9
van replied on Fri, Nov 14 2008 4:28 AM
Thank you Rocky. Problem is solved using managed field.
Page 1 of 2 (16 items) 1 2 Next > | RSS

Copyright (c) 2006-2010 Marimer LLC. All rights reserved.
Email admin@lhotka.net for support.
Powered by Community Server (Non-Commercial Edition), by Telligent Systems