CSLA 4.2 Rules update

The CSLA rule engine had a major change when moving from 3.8.x to 4.0. The major change being that rules is now classes and inheritable whereas the earlier rules were static methods.

For the upcoming CSLA 4.2 release we have made a number of bug fixes plus added some new features that you should be aware of.

1. InputProperties is now also considered Dependencies just as AffectedProperties.

Take for example the validation of StartDate and EndDate where StartDate must be before EndDate.

In CSLA 4.0/4.1 you would have to add:

 1: BusinessRules.AddRule(new LessThan(StartDateProperty, EndDateProperty));
 2: BusinessRules.AddRule(new Dependency(EndDateProperty, StartDateProperty));
 3:  
 4: BusinessRules.AddRule(new GreaterThan(EndDateProperty, StartDateProperty));
 5: BusinessRules.AddRule(new Dependency(StartDateProperty, EndDateProperty));

In order to make the validation rules execute as expected, ie,  StartDateProperty must be validated when StartDate is changed or EndDate is changed (Dependency) and EndDate vice versa.
It’s very easy to miss the dependencies so in CSLA 4.2 you will only need:

 1:  BusinessRules.AddRule(new LessThan(StartDateProperty, EndDateProperty));
 2:  BusinessRules.AddRule(new GreaterThan(EndDateProperty, StartDateProperty));

And both rules will be executed when either of the property values is changed.

Having Dependency rules will NOT break your code so the code sample with Dependency rules will run exactly the same way as the last sample.

2. Provide overloads to set your own message text

When you register a rule you may now also specify either a constant message string (MessageText) or a lambda expression (MessageDelegate) to fetch a string from a resource file.
Using the lambda version you can support translation/localization of the message.

NOTE: The formatting function is supported in just the same way as the default messages.

Using a constant string:

 1:       BusinessRules.AddRule(new Required(NameProperty){MessageText = "Customer must have name"});
 2:       BusinessRules.AddRule(new MaxLength(NameProperty, 50){Priority = 1, MessageText = "{0} cannot be longer than {1} chars"});
 3:       BusinessRules.AddRule(new MinValue<int>(Num1Property, 5) { MessageText = "Num1 must be larger than or equal to {1}" });

Using a lambda to support localization:

 1:       BusinessRules.AddRule(new Required(NameProperty){MessageDelegate = () => Resources.NameRequired});
 2:       BusinessRules.AddRule(new MaxLength(NameProperty, 50){Priority = 1, MessageDelegate = () => Resources.NameMaxLength});
 3:       BusinessRules.AddRule(new MinValue<int>(Num1Property, 5) { MessageDelegate = () => Resources.Num1MinValue });

This properties/delegate is implemented in the base classes, your own rules must make sure to override the GetMessage method to provide the default (and preferably) localizable message text.

 1:     protected override string GetMessage()
 2:     {
 3:       return HasMessageDelegate ? base.GetMessage() : Resources.AnyRequiredRule;
 4:     }

3. Rules for affected properties on an Async rule is automatically rerun and UI notified when context.Complete is called.

This was missing pre 4.2 so when an async rule would lookup information in a database and set a number of
properties in the <bo> the rules for those properties were not rechecked. This would cause the field content
and error/warn/info messages to get out of sync.

4. Rules can now specify conditions for when the rule is not allowed to run.

Typical usage is for any rule that you will only allow to run when the user edits a value and f.ex do an async lookup to a database.
By default and to not brake any existing code, rules will run without restrictions.

Flag Property rule Object rule
CanRunInCheckRules         Yes           Yes
CanRunAsAffectedProperty         Yes           No
CanRunOnServer         Yes           No

Object level rules is executed when either <bo>.CheckObjectRules() or <bo>.CheckRules() is called.

PropertyRules is executed when either <bo>.PropertyHasChanged(property) or <bo>.CheckRules() is called.
And PropertyHasChanged is implicitly called when SetProperty changes a value so most developers will never call PropertyHasChanged.

CanRunInCheckRules when false means that the rule will not be executed when <bo>.CheckRules() is called.

CanRunAsAffectedProperty when false means that this property rule will not run when this property is an affected property on another property rule.

CanRunOnServer when false means that this property rule will not run on the “logical” server side, by all practical means in Data Access Layer.
So when CheckRules is called from Data Access do not execute this rule when set to false.

5. New rule base classes

Two new base classes is introduced:

Class name Description
ObjectRule For an object level rule. Will always have PrimaryProperty = null.
PropertyRule For a property level rule. Must always have a PrimaryProperty.

 

All Property validation rules shipped in CommonRules have this inheritance hierarchy:

BusinessRule
– PropertyRule
– CommonBusinessRule
– Actual Rule

So if your own rules inherit from CommonBusinessRule then you will automatically inherit from PropertyRule.

To make this easier for your developers consider adding your own base classes like:

 1:   public abstract class SyncLookupRule : CommonBusinessRule
 2:   {
 3:     protected SyncLookupRule(IPropertyInfo primaryProperty) : base(primaryProperty)
 4:     {
 5:       IsAsync = false;
 6:       CanRunAsAffectedProperty = false;
 7:       CanRunInCheckRules = false;
 8:       CanRunOnServer = false;
 9:     }
 10:   }

and/or

 1:   public abstract class AsyncLookupRule : CommonBusinessRule
 2:   {
 3:     protected AsyncLookupRule(IPropertyInfo primaryProperty) : base(primaryProperty)
 4:     {
 5:       IsAsync = true;
 6:       CanRunAsAffectedProperty = false;
 7:       CanRunInCheckRules = false;
 8:       CanRunOnServer = false;
 9:     }
 10: 

for Sync and Async lookup rules that will only run when a property is changed by user or code.

Identify patterns and stereotypes of rules in your app and create more base classes. You can share them with us on the CslaContrib site

6. Both Object and Property rules can set Error/Warning/Info on other properties, and even co-exist.

This feature would seem possible with Csla 4.0/4.1 but never worked properly. These issues are now fixed and will even allow a
property level rule to set error/warning/info message on other properties than the Primary property.

This AnyRequired rule will make sure that at least one of the fields must have a value and if broken sets ErrorMessage on all fields:

 1:   public class AnyRequired : CommonBusinessRule
 2:   {
 3:     public AnyRequired(IPropertyInfo primaryProperty, params IPropertyInfo[] additionalProperties)
 4:       : base(primaryProperty)
 5:     {
 6:       InputProperties = new List<IPropertyInfo>() {primaryProperty};
 7:       InputProperties.AddRange(additionalProperties);
 8:     }
 9:  
 10:     protected override string GetMessage()
 11:     {
 12:       return HasMessageDelegate ? base.GetMessage() : Resources.AnyRequiredRule;
 13:     }
 14: 
 15:     protected override void Execute(RuleContext context)
 16:     {
 17:       // if all values are Null or Empty
 18:       if (context.InputPropertyValues.Select(keyvalue => keyvalue.Value.ToString().Trim()).All(string.IsNullOrEmpty))
 19:       {
 20:         var fieldNames = string.Join(", ", context.InputPropertyValues.Select(p =>p.Key.FriendlyName));
 21:  
 22:         foreach (var field in context.InputPropertyValues)
 23:         {
 24:           context.Results.Add(new RuleResult(this.RuleName, field.Key, string.Format(GetMessage(), fieldNames)) { Severity = this.Severity });
 25:         }
 26:       }
 27:     }
 28:   }

usage:
 1:     BusinessRules.AddRule(new AnyRequired(NameProperty, Name2Property, Name3Property));

Summary

This post gives a brief introduction to the enhancements that is made in the Rule engine for Csla 4.2.

Stay tuned for more updates. My next posts will dive into different types of rules and how to implement these.
All code I use here can be found the RuleTutorial sample that will be in the Samples download.
At time of publishing this sample is not included in the Csla 4.2 Alfa release so you must get latest version from trunk to get the code.

Advertisements

2 thoughts on “CSLA 4.2 Rules update

  1. Pingback: rick otton

  2. Pingback: Project Torque

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s