As i didn't saw a easy way to add a business rule like:
- "Required unless is other property is null"
- "Required when other property value equals"
I guess that the most common solution would be writing a new Business Rule inheriting from the Required Rule, having another property as a secondary property which will be read and compared with some other value.Or create a business rule when something is a little bit differend. EnddateIsNullOrNotInThePastRule, EnddateIsInThePastRule
For those little differences i created a wrapper business rule that can be around the actual business rule which behavious like a comparer. If .... then execute the actual rule
Is this something that can be taken into Csla itself, maybe in a better (more complete) implementation ?
/// <summary>
/// Business rule condition wrapper to execute an inner rule when condition is met.
/// </summary>
public class ConditionRule<T, V> : BusinessRule
where T : IBusinessRule
{
/// <summary>
/// Creates an instance of the rule.
/// </summary>
/// <param name="rule">Rule to execute when condition is met.</param>
/// <param name="conditionProperty">Condition value property.</param>
/// <param name="conditionValue">Condition comparer function.</param>
public ConditionRule(PropertyInfo<V> conditionProperty, Func<V, bool> conditionFunc, T rule)
: base(conditionProperty)
{
ConditionProperty = conditionProperty;
PrimaryProperty = rule.PrimaryProperty;
this.RuleUri = new RuleUri(rule, PrimaryProperty);
InputProperties = new List<Csla.Core.IPropertyInfo> { conditionProperty };
ConditionFunc = conditionFunc;
Rule = rule;
InputProperties.AddRange(Rule.InputProperties);
}
/// <summary>
/// The rule to execute when the condition compared true.
/// </summary>
private IBusinessRule Rule { get; set; }
/// <summary>
/// Condition function to execute the comparisation.
/// </summary>
private Func<V, bool> ConditionFunc { get; set; }
/// <summary>
/// Rule implementation.
/// </summary>
/// <param name="context">Rule context.</param>
protected override void Execute(RuleContext context)
{
var value = (V)context.InputPropertyValues[ConditionProperty];
if (ConditionFunc(value))
{
Rule.Execute(context);
}
}
}
using it like this:
protected override void AddBusinessRules()
{
// RejectReason is required when IsRejected is true
BusinessRules.AddRule(new ConditionRule<Required, bool>(
IsRejectedProperty, (p) => p == true,
new Required(RejectReasonProperty)));
// When enddate is not null it should not be in the past
BusinessRules.AddRule(new ConditionRule<IsDateNotInThePastRule, DateTime?>(
EnddateProperty, (p) => p != null,
new IsDateNotInThePastRule(EinddatumProperty)));
}
Hi rfcdejong
I have tried this. But there is a problem:
In the Execute of the ConditionRule you call Execute of the InnerRule with the Context of the ConditionRule. Now, for example the Required-Rule reads the InputValueProperty out of the Context - but this Property is not in this context. Do you see, what I mean?
Best Regards, Thomas
In all essence - we are here looking at a rule with RuleChaining.
There is a couple of lines missing (bugs) in that code:
1. Code must add InputProperties from the inner rule in constructor.
public ConditionRule(PropertyInfo<V> conditionProperty, Func<V, bool> conditionFunc, T rule)
: base(conditionProperty)
{
InputProperties = new List<Csla.Core.IPropertyInfo> { conditionProperty };
ConditionFunc = conditionFunc;
Rule = rule;
InputProperties.AddRange(Rule.InputProperties);
}
2. Execute must use GetChainedContext:
protected override void Execute(RuleContext context)
{
var value = (V)context.InputPropertyValues[PrimaryProperty];
if (ConditionFunc(value))
{
Rule.Execute(context.GetChainedContext(Rule));
}
}
Thomas, yes, i see what u mean.. It's one of the reasons i post this here and i see Jhonny saw the bugs.
I'm going to adjust my code and impliment it further.
I changed the line of adding inputproperty's to:
InputProperties.AddRange(Rule.InputProperties.Where(x => !InputProperties.Contains(x)));
This way a duplicate InputProperty won't cause an exception in method BusinessRules.RunRules at line 520 adding the property item twice in the values dictionary.
Jhonny, do u think something like this should be in Csla or perhaps CslaContrib ?
Hi,
I'd love to see this kind of contributions going into CslaContrib. We need more activity and contributions going into CslaContrib and Rules is definitely one new area we should be able to share code.
Rocky is in charge for what goes into CSLA or not so that's his call
Copyright (c) Marimer LLC