Vibrant discussion about CSLA .NET and using the framework to build great business applications.
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.
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
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
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.
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 =
Sednica.DokumentList = documentList;
Sednica = Sednica.Save();
documentList is same object as a Sednica.DokumentList
If the object isn’t dirty it won’t be saved. You need to ensure that the newly added object is dirty.
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 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
{
_prisutnostList =
}
SetProperty<
//void _prisutnostList_ListChanged(object sender, System.ComponentModel.ListChangedEventArgs e)
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.
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.
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.
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!
Yeah, I should have been more clear as to which post I was referencing. Here is the quote that I was referring to:
-----------------------------
tabaguley
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 ;)