CSLA 4 PropertyHasChanged breaking change

CSLA 4 PropertyHasChanged breaking change

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


Jaans posted on Sunday, December 19, 2010

As we are upgrading to CSLA 4, I'm happy to see the changes in CSLA 4 result in much more concise and strongly typed code. I cannot place enough emphasis on the value of this, especially at compile time!

With the Rules system it is a bit more difficult to ensure type safety at compile time especially with regard to getting the property value from the input properties collection from inside the Execute override. This bit requires that you cast the property value to the correct type, but that will give you runtime pain if you changed the type without remembering to update the rule Execute override accordingly. But that's another matter altogether and I digress.

A change I noticed was that PropertyHasChanged now only accepts parameters of type PropertyInfo (instead of string before). This is great and helps guide the framework user to use a more type-safe world and helps trouble scenarios where property names change.

I do have a few scenario's where I have "calculated" properties on business objects. These are really formula fields that I do not want to define as a CSLA Property (ie. RegisterProperty) as they are not stored at all, and should also not be serialized. Often these "calculated/formula" properties are dependent on one or more CSLA properties, and to ensure that any binding to a "calculated/formula" property is updated/refreshed when one/or more of the properties it depends on changes, I could "PropertyHasChanged( "CalculatedProperty" )", or setup a dependency property rule based on the string name of the property.

An example of this is a FullName calculated property that is a combination of Last and Firstname or an Age calculated property that is based on the DateOfBirth property:

        public string FullName
        {
            get { return string.Format"{0}, {1}"LastNameFirstName ); }
        }

        public int AgeInDays
        {
            get { return (DateTime.Now - DateOfBirth).Days; }
        }

From the above, using CSLA 3.8 I would simply setup a dependency property using strings, or if in a business rule I would call PropertyHasChanged("FullName") to let any subscribers know that the value needs refreshing.

Enter CSLA 4 and PropertyHasChanged no longer accepts string based properties - a change I wholeheartedly agree with, especially for the 85% case. I did notice that there is a is an overload for:

OnPropertyChanged( string propertyName )

Would I be able to safely use this for my "edge" cases?

Would there be a case for a Csla.Rules.CommonRules.Dependency rule that accepts a params[] string dependentProperties ?

Just thinking about it, a really powerful one would be to have a PropertyHasChanged overload and something similar for the Dependency rule, that uses a LambdaExpression that points to a property on the same class. (Much like RegisterProperty does). This would really give excellent protection against the magic string literals and solve the limitations I now have. How about that?

Thanks

JonnyBee replied on Monday, December 20, 2010

Hi Jaans,

So you have created a dependency rules that only raise PropertyHasChanged to notify UI?

You should be aware that PropertyHasChanged does a number of operations:

1. Marks the object as dirty
2. Call business rules for the property (and dependencies)
3. Calls OnPropertyChanged to notify UI (and others)

Whereas OnPropertyChanged only does:

1. Raise the PropertyChanged event to notify UI and other event listeners.

So in your edge case - you should safely call OnPropertyChanged as it only raises the PropertyChanged event.

But - even if you create a PropertyInfoObject - you would only allocate an empty slot (null) in FieldManager that would only be a marginal memory consumption unless you load a very large number of objects.

The Rule engine in CSLA4 expects only IPropertyInfo so if you need a rule then you really should declare a PropertyInfo for that object.

So in my opinion - NO we should not create a standard Dependency rule that accepts a params[] string dependentProperties. The existing Dependency rule is there to call Rules on the dependent properties -  this would not work with an array of string propertyNames in the rule engine.

You may create your own "dependency"  type rule that ONLY calls OnPropertyChanged for a params string list of PropertyNames.

One other reason for using PropertyInfos is speed. In the "older" Csla version we had a PropertyHasChanged() - no parameters - that used reflection to get the PropertyName. That was incredibly expensive !!! Having a PropertyHasChanged that accepts a Lambda expression may seem good but might actually use refection to get propertyname and be quite slow. That is one of the reasons the PropertyInfo object was introduced - to keep the propertyName in a static variable and not use reflection on every call.

 

Jaans replied on Monday, December 20, 2010

Hi Jonny

Thank your for the detailed info.
It helps to know how the two implementations differ, and that I could go with "OnPropertyChanged" for lightweight change notification in scenarios where I want to be very "light" (at the cost of maintainability).

So you are you saying that if I create a CSLA PropertyInfo property (using RegisterProperty) for these "calculated/formula" properties but never actually load a value into the property (via LoadProperty / SetProperty) that the FieldManager would be holding a NULL value for the field and thus it's "payload" with regard to memory and serialisation is slight?

If that's the case and depending on how small the "payload" is, it would be preferable to do it this way. That simplifies things a lot and avoids my inelegant suggestion. Futhermore it enables scenarios where I could attach rules to such "calculated" properties.

A PropertyInfo implementation would cover the "90% case", but on the odd occasion keeping that serialization payload / memory footprint low is a primary goal and so it would be useful to try and quantify the cost of this "empty slot". Any indication of what that may be?

Thanks again,
Jaans

JonnyBee replied on Monday, December 20, 2010

Hi Jaans,

FieldDataManager allocates a memory slot of type: 

 [NonSerialized()]
 private List<IPropertyInfo> _propertyList;
private IFieldData[] _fieldData;
 _fieldData = new IFieldData[_propertyList.Count];

But the actual fielddata for a property is created the first time a property is reference (in f.ex LoadProperty,  GetProperty, ReadProperty or SetProperty).
The FieldDataManager also hold a consolidated list of PropertyInfos but is this list is not serialized over the wire.

So it should be a minimal impact in memory.

Copyright (c) Marimer LLC