@snikolayev Is there any way to have an "optional" clause in the NRules DSL that would serve only to fill a value if the clause evaluates to true? I know this would be possible via an Or clause where one of the branches of the Or always evaluates true, but I'd prefer not to have the rule fire twice if the optional branch evaluates true.
e.g. see this pseudocode:
Define() {
bool foundOptionalFact = false;
When()
// Required clause
.Exists<FactType>(fact => fact.PassesTest)
// Optional clause
.Optional(x => x
.And(xx => xx
.Exists<FactType>(fact => fact.PassesTest)
.Let(() => foundOptionalFact , () => true)));
// Then will run as long as long the required clause is evaluated true
Then(ctx => (foundOptionalFact));
}
Hey @larrybehlang_gitlab - the way you handle optional facts normally is via a Query. Something like this:
Fact1 requiredFact = default;
Fact2 optionalFact = default;
When()
.Match<Fact1>(() => requiredFact, c => c.Predicate)
.Query(() => optionalFact, x => x
.Match<Fact2>(o => o.Predicate)
.Collect()
.Select(c => c.FirstOrDefault()));
You can also wrap this into an extension method, call it MatchOptional and have a shorthand for it
NRules.Language
like there is for Fluent Rules DSL
https://github.com/NRules/NRules/wiki/Fluent-Rules-DSL
NRules.Language
for NRules, and I have a question - is there a way to organise my rules in to separate RuleSets
(like the option present in the DSL version of NRules)? I tried adding ruleset
in the rul file but it doesn't like that.
Is there a way to write DLC-extensions in a way that allows passing the value bound to a variable via Let
to the extension?
For example, I would like to change
public class MyRule : Rule
{
public override void Define()
{
MyClass obj = null;
When()
// [...]
.Let(() => obj, () => GetMyClassInstance())
.Having(() => ValidateMyClass(obj))
// [...]
}
}
into
public class MyRule : Rule
{
public override void Define()
{
MyClass obj = null;
When()
// [...]
.Get(() => obj)
.Validate(obj)
// [...]
}
}
public static class DslExtensions
{
public static ILeftHandSideExpression Get(this ILeftHandSideExpression lhs, Expression<Func<MyClass>> alias)
{
return lhs
.Let(alias, () => GetMyClassInstance())
;
}
public static ILeftHandSideExpression Validate(this ILeftHandSideExpression lhs, MyClass objectToValidate)
{
return lhs
.Having(() => ValidateMyClass(objectToValidate)) // objectToValidate is always null here
;
}
}
However, this will not work because objectToValidate
will always be null.
It will "work" if I either rename the parameter to be the same as in the Rule ("obj
") or remove the parameter alltogether and instead add a local object by that name to the Validate
function:
public static ILeftHandSideExpression Validate(this ILeftHandSideExpression lhs, MyClass obj)
{
return lhs
.Having(() => ValidateMyClass(obj)) // this "works"
;
}
public static ILeftHandSideExpression Validate(this ILeftHandSideExpression lhs)
{
MyClass obj = null;
return lhs
.Having(() => ValidateMyClass(objectToValidate)) // so does this
;
}
This obviously removes quite a bit of flexibility from working with DSL-extensions so I suspect there is some way to do what I am trying here that I am not aware of?