I am dealing with a network of rules that change values of a businessobject. When a rule has run, I am only interested in outputvalue/properties that are changed, so I can run rules on them as well.
In BusinessRules.RunRules() LoadProperty is called for every added OutputPropertyValue. Then all OutputPropertyValues are added as AffectedProperties, even if their targetvalue has not changed.
I tried to overrule this behaviour by getting the original value of the OutputPropertyValue and only call AddOutValue() when the value has changed. Because BusinessRules.RunRules(0 also adds all AffectedProperties declared at the rule to the AffectedPropertis, I cleared them all out. The problem is now that I can't add those properties as outputvalue, because AddOutValue() needs them to be declared in Rule.AffectedProperties.
Why are rule.AffectedProperties and context.OutputPropertyValues both added to the affectedProperties of RunRules() when the first contains the second?
There's more reasons to it than just running rules for properties that was changed.
AffectedProperties is also used for:
So we are not going to change this behavior.
I am however working on changes for CSLA 4.5 to make properties become dirty when value is changed by AddOutValue.
Today values that are changed as AddOutValue will NOT be marked as dirty as the rule engine uses LoadProperty.
Even if affectedProperties may be added un-neccessarily it is not much of a performance problem as the rule engine will return "distinct" of the list.
The rule engine will automatically rerun rules for AffectedProperties but ONLY for the "top" - the origin property. You do NOT need to rerun rules for the affected properties manually. When rules are rerun for AffectedProperties they will not cascade on to AffectedProperties of the AffectedProperties as this may cause infinite loops. Is this what you are trying to do?
You should also be aware that a rule (inherit from PropertyRule or CommonBusinessRule) may also set:
These properties is very useful for asyncronous rules that you typically only want to run in client side of DataPortal and only when user edits values.
What I try to do is cascade on changed properties. The rules have circular references, that are known to end. After a call to BusinessRules.CheckRules(IPropertyInfo), all I need to know is what properties have changed, so I can recurse to rule checking for only those properties.
I solved my problem by not only overriding PropertyHasChanged, but also LoadProperty. Before the call to Business.CheckRules(IPropertyInfo) a List<string> is created. In LoadProperty propertyInfo.Name is added to that list when the property has changed. When BusinessRules.CheckRules(IPropertyInfo) ends, I recurse into the changed properties other than the current. It might be a not to elegant solution, but it works.
If you create dependencies (like AffectedProperties) the rule engine will automatically rerun rules for ALL affected properties.
a property (1) is set (base.PropertyHasChanged(property) is called:
- run rules for this property (1) with cascade = true - returns list of AffectedProperties (including property (1))
- run rules for all properies in AffectedProperties, except property (1), with cascade = false
- raise OnPropertyChanged for all properties to notify UI and Controls.
You should normally not need to do this hack to cascading execution of rules if you can define proper Dependencies.
I think i do understand the problem that WimN has. It seems that the rule engine stops after rerunning the business rules for affected properties. (with cascade = false, as cascade is only one level).
PS: I just wrote an example which is not ver clear, so i deleted it.
It is a scenario where some financial properties are recursively being calculated each time. The properties are heavy dependent on eachother and is affecting the other properties. When the outputvalue is not changing anymore then the rule engine should stop processing.
I'll try to post an example later.
Starting with CSLA 4.5 there is a new method called from the rule engine to update the BO on AddOutValue that will actually make the field Dirty if the value changed. This MIGHT be returned to rule rule engine to that it could rerun the rules with Cascade option if the value was changed.
This is NOT implemented yet - however I am considering this shift.
What do you think?
Currently i still can't explain the situation, but if your consideration of this shift will be 'YES' it would definitly fix some code that is currently implemented as work around in the abstraction layer.
I think there must be a way to tell the rule engine to continue executing rules as long as the values change. Perhaps continue cascade until no more values change might be better as default.
I do think "yes", and i hope it would be even possible to merge it into CSLA 4.3
Currently we aren't using the Rule Engine anymore, but instead the PropertyHasChanged method is overriden in an abstraction layer where foreach property the business rules are invoked. This leads to several problems, but for now it's the only way to let business rules work the way they are including cascadeing on.
The problem is that there are LOTS of properties that have to be recalculated when the value of another property has changed. And only when it has changed, not on a OnPropertyChanged. That way it's often that multiple cascading is needed until no more values change.
Are you still conidering the shift? I would like to comply to CSLA standards, so that we can use the community to find answers. Now our 'framework' works differend.
Raymond de Jong
We're discussing this item as to whether or not to include this and if we should provide a configurationsetting for "CslaRulesCascadeOnOutValueChanged" to activate this behavior (default would be off).
Copyright (c) Marimer LLC