BusinessRules based on a originating property

BusinessRules based on a originating property

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


rfcdejong posted on Wednesday, July 25, 2012

colleague of my has a issue having editable 'calculated' properties where an user can change the value of several properties which has business rules.

BO with 5 properties
Startdate
Years
Months
Duration (total months)
Enddate


Scenarios
When enddate has changed and when the startdate has a value
Then the years, months and duration properties should be calculated.

When startdate has changed and when the enddate has a value
Then the year, months and duration properties should be calculated.

When years property has changed and startdate has a value
Then the duration and enddate property should be calculated.

When months property has changed and startdate has a value
Then the years, months (if >= 12), duration and enddate should be calculated.

And some others.

Not working solution
See the attachment as a test project.
Currently my 
colleague made 4 businessrules

CalcDurationFromDates
CalcDurationFromYearsAndMonths
CalcEnddate
SplitIntoYearsAndMonths

 

What is the recommended way to solve this without having to 'hack' and setting a custom 'originating property'? Maybe just one businessrule instead of the 4 differend business rules?

JonnyBee replied on Wednesday, July 25, 2012

This is a classic case of rules that should run on the client side only and when a given property is changed.

The recommended way is to create e "generic" rule that allows you to specify the PrimaryProperty to act upon as a separate parameter and set

      CanRunAsAffectedProperty = false;
      CanRunOnServer = false;
      CanRunInCheckRules = false;

Take f.ex the CalcEndDate rule I would write as:

  public class CalcEnddate : CommonBusinessRule
  {
    private readonly IPropertyInfo _enddateProperty;
    private readonly IPropertyInfo _startdateProperty;
    private readonly IPropertyInfo _durationProperty;
 
    public CalcEnddate(IPropertyInfo primaryProperty, IPropertyInfo enddateProperty, 
IPropertyInfo startdateProperty, IPropertyInfo durationProperty)       : base(primaryProperty)     {       InputProperties = new List<IPropertyInfo> { startdateProperty, durationProperty };       AffectedProperties.Add(enddateProperty);       _enddateProperty = enddateProperty;       _startdateProperty = startdateProperty;       _durationProperty = durationProperty;       CanRunAsAffectedProperty = false;       CanRunOnServer = false;       CanRunInCheckRules = false;     }     protected override void Execute(Csla.Rules.RuleContext context)     {       int? duration = context.InputPropertyValues[_durationProperty] as int?;       DateTime? startdate = context.InputPropertyValues[_startdateProperty] as DateTime?;       if (!duration.HasValue || !startdate.HasValue) return;       context.AddOutValue(_enddateProperty, startdate.Value.AddMonths(duration.Value));     }   }

And add as

BusinessRules.AddRule(new CalcEnddate(StartdateProperty, EnddateProperty, StartdateProperty, DurationProperty));
BusinessRules.AddRule(new CalcEnddate(DurationProperty, EnddateProperty, StartdateProperty, DurationProperty));

Yes, this requires one instance of the rule for each field to act upon as they are edited by the user.

The end result: You can instruct the rule engine that this rule is to be run ONLY when PrimaryProperty is changed by the user and NOT on the logical serverside, in CheckRules or as an AffectedProperty from another rule.

Using this technique you decouple what the rule actually does (as transformation) from the PrimaryProperty it acts upon. This is important as in this case you only want the actual transformation rule to run for the Property that was changed.and let the RuleEngine revalite the updated fields afterwards.

rfcdejong replied on Thursday, July 26, 2012

I might not completely understand, but is it not that when the duration changes by a business rule then the enddate calculation is an affected property?
I'm currently trying to change the code so that it works in all situations, but until now i'm not successfull.

JonnyBee replied on Thursday, July 26, 2012

The challenge of having fields that update each other is that you have no control over the sequence of execution for the AffectedProperties. So chances are that another rule updates and resets the last edit by user.

That is why I prefer to have these rules run as a "propertyrule" on each field so I can be sure that these rules executes and updates the other fields based on the last edit - and than revalidate the AffectedProperties.

 

rfcdejong replied on Thursday, July 26, 2012

I got it working with some modifications, see the attachment.
Most of the business rules have now CanRunAsAffectedProperty set to false.

In the business object they are added forearch property that can be changed.

It works, but indeed it has a risk when properties update other properties. And there is a learning curve some people just hack around. 

Copyright (c) Marimer LLC