Rules triggering other rules

Rules triggering other rules

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


dan_morey posted on Tuesday, December 13, 2011

I have a business object set up with properties AAA, BBB, CCC and DDD.

I wanted to blank the latter properties if early ones get changed.  This means if BBB changes, AAA will be left alone and CCC and DDD will be set to nothing.

I have set up a rule so that if AAA changes it sets BBB to Nothing, another rule sp that if BBB changes it sets CCC to Nothing and yet another if CCC changes it sets DDD to nothing.

When I try this out and set AAA to a value, it sets BBB to nothing, that then sets CCC to nothing, but then DDD is not set to nothing.

I've looked on the forum and noticed that it says that rules use LoadProperty and so shouldn't trigger other rules, but my first rule is triggering the second, but my second is not triggering the third.

This has made me unsure what to do. If I leave it as is, the last rule doesn't run, but if I modify the rules so changing AAA set BBB, CCC and DDD to nothing then it could still trigger another rule.

Does anyone why this is happening? I would expect it to either not trigger other rules, or always trigger other rules. Not trigger the next layer of rules but not the next layer after that. I hope I'm making sense.

 

RockfordLhotka replied on Tuesday, December 13, 2011

Rules only cascade one level. So if BBB is dependent on AAA, then when rules for AAA run so will BBB rules. But that's as far as it goes.

In other words, if CCC is dependent on BBB those rules won't run because of AAA changing.

If you want to change AAA and have all those other properties run their rules, make them all dependent.

JonnyBee replied on Tuesday, December 13, 2011

Like Rocky stated - affected properties is only checked on the "next" level.

Assuming the fields are all string fields you could write a generic rule like this:

  public class ClearFields : Csla.Rules.PropertyRule
  {
      public ClearFields (IPropertyInfo primaryProperty, params IPropertyInfo[] additionalFields) : base(primaryProperty)
      {
          if (additionalFields == null)
              throw ArgumentNullException("additionalFields cannot be null");
          
          AffectedProperties.AddRange(additionalFields)
          CanRunOnServer = false;
          CanRunInCheckRules = false;
          CanRunAsAffectedProperty = false;
      }
      
    protected override void Execute(RuleContext context)
    {
        foreach (var element in this.AffectedProperties) 
        {
            context.AddOutValue(element, string.Empty);
        }
    }
  }

 

Usage:

      BusinessRules.AddRule(new ClearFields(AAAProperty, BBBProperty, CCCProperty, DDDProperty);
      BusinessRules.AddRule(new ClearFields(BBBProperty, CCCProperty, DDDProperty);
      BusinessRules.AddRule(new ClearFields(CCCProperty, DDDProperty);

 

The ClearFelds rule will only run when the property is changed by the user (or code on the client side of DataPortal)  and rules for additional fields will be rechecked as they are in the AffectedProperties list.

dan_morey replied on Tuesday, December 13, 2011

I've set up a dependency between AAA and CCC. Now when AAA is changed ,the rule will blank BBB and the BBB rule will blank CCC. The dependency then kicks off the CCC rule to blank DDD. This seems to work fine for what I am doing here.

I like what you have put JonnyBee as It looks like I can set up the rules so that just the one will run. I'll just have to set everything I need to blank in the one rule, and have a rule for each of the three properties as I have now (but with more properties). However, I'm not entirely sure what the three properties are that you have used.

CanRunOnServer, sounds like it won't trigger if it's on the server (i.e. gone through the data portal).

CanRunAsAffectedProperty, sounds like it prevents this rule running if it would have been trigger by another rule (the one cascade level that Rocky mentions). This looks like the most important for what I need.

I have no idea what CanRunInCheckRules. What does this do (and the others if I am wrong in my assumptions).

JonnyBee replied on Tuesday, December 13, 2011

CanRunInCheckRules web falske will prevent the rule from running when CheckRules is called.

The end result when all is false is that the rule only runs when user update a field.

cornel_md replied on Wednesday, January 11, 2012

Hi All,

I was just wondering if it is a problem to update the latter properties in the property setter, rather than creating a PropertyRule for this particular case.

Ex:

public static readonly PropertyInfo AAAProperty = RegisterProperty(c => c.AAA);
public string AAA
{
	get { return GetProperty(AAAProperty); }
	set { 
		SetProperty(AAAProperty, value); 
		BBB = string.Empty;
	}
}
public static readonly PropertyInfo BBBProperty = RegisterProperty(c => c.BBB);
public string BBB
{
	get { return GetProperty(BBBProperty); }
	set { 
		SetProperty(BBBProperty, value); 
		CCC = string.Empty;
	}
}
public static readonly PropertyInfo CCCProperty = RegisterProperty(c => c.CCC);
public string CCC
{
	get { return GetProperty(CCCProperty); }
	set { 
		SetProperty(CCCProperty, value); 
		DDD = string.Empty;
	}
}
public static readonly PropertyInfo DDDProperty = RegisterProperty(c => c.DDD);
public string DDD
{
	get { return GetProperty(DDDProperty); }
	set { SetProperty(DDDProperty, value); }
}

Thanks

JonnyBee replied on Wednesday, January 11, 2012

Hi,

I try to always remind my developer that properties is not code.

The reason being that I like my data access to look like this:

using (BypassPropertyChecks())
{
      DDD = "a";
      CCC = "b";
      AAA = "c";
      BBB = "e";
}

and not have to worry about the sequence of property setters and have the same syntax for both managed fields and private fields.

The alternative would be :

LoadProperty(DDDProperty, "a");
LoadProperty(CCCProperty, "a");
LoadProperty(AAAProperty, "a");
LoadProperty(BBBProperty, "a");
_EEEfield = "e";

You should also be aware that some databinding tecnologies will ALWAYS call the setter - no matter if the value was changed or not. So simply tabbing around without changing any value in the UI would clear fields. CSLA Rules on the other hand will be triggered when field value is changed and is a much better approach.

Also - for a web app using the DataMapper or CslaModelBinder you would not want to care about the sequence of setters when updating your model.

Copyright (c) Marimer LLC