Rules with affected properties calling rules with affected properties

Rules with affected properties calling rules with affected properties

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


decius posted on Thursday, February 16, 2012

I noticed today that if I have a rule that contains affectedproperties, that then runs the affected rule, if the "parent" rule here does not contain all the affected properties of the "child" rule, then the NotifyProperty changed event never occurs for the "child" affected property. Is this by design? Or do I just have to remember to add the affected property to the parent rule? Seems like that would be easy to forget in complex logic situations.

To illustrate, take the following code. I've found that if MainProperty is changed, it will call the affected property rule as expected, and even though that rule contains the SomeOtherProperty as an affected property it will not notify the UI of a property changed event. 

The AddOutValue does not throw an exception and runs fine though. And if I add the affected SomeOtherProperty to the AffectsRule, everything works fine.

 

protected override void AddBusinessRules()
		{
			BusinessRules.AddRule(new AffectsRule(MainProperty, AffectedProperty));
			BusinessRules.AddRule(new AffectedRule(AffectedProperty, SomeOtherProperty));
		}

		private class AffectsRule : Csla.Rules.BusinessRule
		{
			public AffectsRule(IPropertyInfo primaryProperty, IPropertyInfo affectedProperty)
				: base(primaryProperty)
			{
				AffectedProperties.Add(affectedProperty);
			}
		}

		private class AffectedRule : Csla.Rules.BusinessRule
		{
			IPropertyInfo SomeOtherProperty {get;set;}

			public DependsOnAmountRule(IPropertyInfo primaryProperty, IPropertyInfo someOtherProperty)
				: base(primaryProperty)
			{
				AffectedProperties.Add(someOtherProperty);
				SomeOtherProperty = someOtherProperty;
			}
			
			protected override void Execute(RuleContext context)
			{
				context.AddOutValue(SomeOtherProperty, "no exception, but won't notify UI of change");
			}
		}

JonnyBee replied on Thursday, February 16, 2012

Hi,

By design - the rule engine will only rerun (cascade) rules for the next level of affected properties. This is to avoid circular references and eternal loops.

BTW: Your AffectsRule is identical to the Dependency rule in CSLA. 

What type of UI are you using and what is the setting of CslaPropertyChangedMode in your config file?

I would expect the RuleEngine to include the affected properties of AffectedProperty when MainProperty has been changed and notify ui by raising OnPropertyChanged event.

With the latest code (4.3.2) I get PropertyChanged for all 3 properties when I change Main.

Code ( a  root object with 3 properties and your rules):

      var root = Root.NewEditableRoot();
      root.PropertyChanged += (o, e) =>
                                {
                                  Debug.Print("Property changed: {0}", e.PropertyName);
                                };
      root.Main = 23;
      Debug.Print("Rules completed");

Gives this output:

Property changed: Main
Property changed: Affected
Property changed: SomeOther

using the default value for CslaPropertyChangedMode.

decius replied on Friday, February 17, 2012

Thanks, I'm just using WPF4, though I am on an older CSLA 4.1, which I don't see opportunity any time soon to upgrade that for my project.

Still, I'm just a little confused by your response; you say this is by design, but also that OnPropertyChanged happens for you for all 3 properties when changing main. So do I have to add SomeOtherProperty to the affected properties of AffectsRule or not? I think if I understand you correctly from the "cascaded rules" comment, I'll have to add SomeOtherProperty to the affected Properties of the AffectsRule correct?

Yeah, I certainly understand the circular execution paths that the new sub system is trying to avoid. I've got myself pretty confused with the past system at times. Though, the new system is just taking some getting used to; dependency logic has been a little struggle to get my brain to work this way.

For me, if SomeOtherProperty is not added to the AffectsRule's AffectedProperties, OnPropertyChanged does not get called for SomeOtherProperty when changes are made to MainProperty.

Thanks for pointing out the Dependency class. After the CSLA 3.8-4 transition, that wasn't clear to me so I've just been making my own. 

JonnyBee replied on Friday, February 17, 2012

Yes, then it is a bug that has been fixed since 4.1.

The rule engine should raise OnPropertyChanged for all affected properties.

Here is the source:

    private List<string> CheckRulesForProperty(Csla.Core.IPropertyInfo property, bool cascade)
    {
      var rules = from r in TypeRules.Rules
                  where ReferenceEquals(r.PrimaryProperty, property)
                  orderby r.Priority
                  select r;
      var affectedProperties = new List<string> { property.Name };
      BrokenRules.ClearRules(property);
      affectedProperties.AddRange(RunRules(rules));

      if (cascade)
      {
        // get properties affected by all rules
        var propertiesToRun = new List<Csla.Core.IPropertyInfo>();
        foreach (var item in rules)
          foreach (var p in item.AffectedProperties)
            if (!ReferenceEquals(property, p))
              propertiesToRun.Add(p);
        // run rules for affected properties
        foreach (var item in propertiesToRun.Distinct())
          affectedProperties.AddRange(CheckRulesForProperty(item, false));  // this is the only changed required for this fix
      }

      return affectedProperties.Distinct().ToList();
    }

I would recommend an upgrade tho'. There's several important bugfixes that you should consider for your project, especially when CSLA 4.3 is released soon.

Copyright (c) Marimer LLC