Hi All,
We have constructed a generic class project to house common BusinessRules. In particular we have a BusinessRule to ensure a To date always follows a From date (from 2 DateTimePicker controls). I have placed break points on the execute methods but the rule is never fired.
In the AddBusinessRules we have the following:
BusinessRules.AddRule(
new X.Y.BusinessRules.StartDateBeforeEndDate(ActiveFromProperty, ActiveToProperty));
Our generic class project containing the business rule looks like:
namespace X.Y.BusinessRules
{
[Serializable()]
public class StartDateBeforeEndDate : BusinessRule
{
IBusinessRule _innerRuleStartDate;
IBusinessRule _innerRuleEndDate;
public StartDateBeforeEndDate(IPropertyInfo startDateProperty, IPropertyInfo endDateProperty)
{
_innerRuleStartDate = new InnerStartDateBeforeEndDate(startDateProperty, startDateProperty, endDateProperty)
{
AffectedProperties = {endDateProperty}
};
_innerRuleEndDate = new InnerStartDateBeforeEndDate(endDateProperty, startDateProperty, endDateProperty)
{
AffectedProperties = {startDateProperty}
};
InputProperties = new List<IPropertyInfo>() { startDateProperty, endDateProperty };
}
protected override void Execute(RuleContext context)
{
_innerRuleStartDate.Execute(context);
_innerRuleEndDate.Execute(context);
}
private class InnerStartDateBeforeEndDate : BusinessRule
{
protected IPropertyInfo StartDateProperty
{
get;
private set;
}
protected IPropertyInfo EndDateProperty
{
get;
private set;
}
public InnerStartDateBeforeEndDate(IPropertyInfo primaryProperty, IPropertyInfo startDateProperty, IPropertyInfo endDateProperty)
: base(primaryProperty)
{
if (startDateProperty.Type != typeof(SmartDate) || endDateProperty.Type != typeof(SmartDate))
{
throw new ArgumentException(Resources.StartDateBeforeEndDateResource.ArgumentException);
}
StartDateProperty = startDateProperty;
EndDateProperty = endDateProperty;
InputProperties = new List<IPropertyInfo>() { startDateProperty, endDateProperty };
}
protected override void Execute(RuleContext context)
{
SmartDate start = (SmartDate)context.InputPropertyValues[StartDateProperty];
SmartDate end = (SmartDate)context.InputPropertyValues[EndDateProperty];
if (start.CompareTo(end) > 0)
{
context.AddErrorResult(string.Format(Resources.StartDateBeforeEndDateResource.RuleMessage, StartDateProperty.FriendlyName, EndDateProperty.FriendlyName));
}
}
}
}
}
Any idea's why this business rule is not firing when the DateTimePicker values are being changed? The field is being databound to the Value property.
What you are trying to do is not the proper way to create business rules.
From the rule engines perspective you have
So you have a "selector" rule with 2 inner rules (rule chaining). Both inner rules MUST be on the same PrimaryProperty and you must call
context.GetChainedContext
to send the rulecontext to your inner rules. This is described in the Csla 4 EBooks by Rocky.
Your must be aware the the rule engine has absolutely no knowledge of your inner rules - it only knows about the rules that are registered in AddBusinessRules.
Another reason the rule may not be firing is how your properties are defined and databinding (especially for Windows Forms) and the conversion of the value from the UI control to the type as defined in your BO. IE: the value may never be set on the property.
From Csla 4.2 you can create this as just one rule that sets Error/Warn/Info on both properties.
Thanks JonnyBee. This class was writen prior to the eBooks being published. I've had my developer rewrite the code which now executes properly. I've posted it here just in case anyone else would like a copy:
[Serializable()]
public class StartDateBeforeEndDate : BusinessRule
{
private IPropertyInfo CompareTo { get; set; }
public StartDateBeforeEndDate(IPropertyInfo primaryProperty, IPropertyInfo compareToProperty)
: base(primaryProperty)
{
if (primaryProperty.Type != typeof(SmartDate) || compareToProperty.Type != typeof(SmartDate))
{
throw new ArgumentException(Resources.StartDateBeforeEndDateResource.ArgumentException);
}
CompareTo = compareToProperty;
if (InputProperties == null)
{
InputProperties = new List<IPropertyInfo>();
}
InputProperties.Add(primaryProperty);
InputProperties.Add(compareToProperty);
}
protected override void Execute(RuleContext context)
{
SmartDate start = (SmartDate)context.InputPropertyValues[PrimaryProperty];
SmartDate end = (SmartDate)context.InputPropertyValues[CompareTo];
if (start.CompareTo(end) > 0)
{
context.AddErrorResult(string.Format(Resources.StartDateBeforeEndDateResource.RuleMessage, PrimaryProperty.FriendlyName, CompareTo.FriendlyName));
}
}
}
I forgot to include the two lines which invoke the rule:
BusinessRules.AddRule(
new Dependency(ActiveToProperty, ActiveFromProperty));
BusinessRules.AddRule(
new Common.Utilities.BusinessRules.StartDateBeforeEndDate(ActiveFromProperty, ActiveToProperty));
You can also find this as a complete ruleset in CslaContrib
LessThan
LessThanOrEqual
GreaterThan
GreaterThanOrEqual
and also all the standard rules in Csla.
These rules also accepts an anonymous method to supply the message from either a custom resource file or a string constant.
Copyright (c) Marimer LLC