DSL Extensions
When defining rules using internal DSL in C#, the DSL is limited to the verbs provided by the rules engine (see Fluent Rules DSL). Consider this example:
[Name("Self insured name validation")]
public class SelfInsuredNameValidationRule : Rule
{
public override void Define()
{
Claim claim = default!;
Patient patient = default!;
When()
.Match<Claim>(() => claim)
.Match<Patient>(() => patient, p => p == claim.Patient,
p => p.RelationshipToInsured == Relationship.Self)
.Match<Insured>(i => i == claim.Insured,
i => !Equals(patient.Name, i.Name));
Then()
.Do(ctx => ctx.Warning(claim, "Self insured name does not match"));
}
}
public static class ContextExtensions
{
public static void Warning(this IContext context, Claim claim, string message)
{
var warning = new ClaimAlert { Severity = 2, Claim = claim, RuleName = context.Rule.Name, Message = message };
context.Insert(warning);
}
}
This rule matches a claim for a self-insured patient, and makes sure that the name of the patient and insured matches. And if the names don't match, it creates a warning-level claim alert.
The rule would look much more readable if we could write it like this:
[Name("Self insured name validation")]
public class SelfInsuredNameValidationRule : Rule
{
public override void Define()
{
Claim claim = default!;
Patient patient = default!;
When()
.Claim(() => claim)
.Patient(() => patient, p => p == claim.Patient,
p => p.RelationshipToInsured == Relationship.Self)
.Insured(i => i == claim.Insured,
i => !Equals(patient.Name, i.Name));
Then()
.Warning(claim, "Self insured name does not match");
}
}
And the good news is that this can be done, by defining DSL extension methods like this:
public static class DslExtensions
{
public static ILeftHandSideExpression Claim(this ILeftHandSideExpression lhs, Expression<Func<Claim>> alias, params Expression<Func<Claim, bool>>[] conditions)
{
return lhs.Match(alias, conditions);
}
public static ILeftHandSideExpression Patient(this ILeftHandSideExpression lhs, Expression<Func<Patient>> alias, params Expression<Func<Patient, bool>>[] conditions)
{
return lhs.Match(alias, conditions);
}
public static ILeftHandSideExpression Insured(this ILeftHandSideExpression lhs, params Expression<Func<Insured, bool>>[] conditions)
{
return lhs.Match(conditions);
}
public static IRightHandSideExpression Warning(this IRightHandSideExpression rhs, Claim claim, string message)
{
return rhs.Do(ctx => ctx.Warning(claim, message));
}
}