I am using NRules to define rules that all inherit from a common base class, which itself inherits from Rule
.
When I use a DSL extension to insert a new fact that wraps a matched object, it seems that the matched object passed to the extension method is null
.
Here's a self-contained example that should demonstrate the problem. I am using the xUnit
test framework to define two rules, each with identical tests. The first one passes, the second one fails.
using NRules;
using NRules.Fluent;
using NRules.Fluent.Dsl;
using Xunit;
using System.Linq;
using System.Reflection;
namespace IntegrationTests.Engine
{
// A simple domain model
public interface IFruit { }
public class Apple : IFruit { }
public class Basket
{
public Basket(IFruit apple)
{
MyApple = apple;
}
public IFruit MyApple { get; private set; }
}
// A base class for the rules
public abstract class RuleBase : Rule
{
public override void Define()
{
// Empty
}
}
// The first rule, which does not use the extension:
public class TestRule : RuleBase
{
public override void Define()
{
base.Define();
Apple a = null;
When()
.Match(() => a);
Then()
.Do(ctx => ctx.Insert(new Basket(a)));
}
}
// The second rule, which uses an extension to add a new fact
public class TestRuleWithExtension : RuleBase
{
public override void Define()
{
base.Define();
Apple apple = null;
When()
.Match(() => apple);
Then()
.AddToBasket(apple);
}
}
// The DSL extension
public static class DslExtensions
{
public static IRightHandSideExpression AddToBasket(this IRightHandSideExpression rhs, IFruit fruit)
{
return rhs.Do(ctx => ctx.Insert(new Basket(fruit)));
}
}
// The tests
public class ExtensionTest
{
// This one tests the first rule and passes
[Fact]
public void TestInsert()
{
//Load rules
var repository = new RuleRepository();
repository.Load(x => x
.From(Assembly.GetExecutingAssembly())
.Where(rule => rule.Name.EndsWith("TestRule")));
//Compile rules
var factory = repository.Compile();
//Create a working session
var session = factory.CreateSession();
//Load domain model
var apple = new Apple();
//Insert facts into rules engine's memory
session.Insert(apple);
//Start match/resolve/act cycle
session.Fire();
// Query for inserted facts
var bananas = session.Query<Basket>().FirstOrDefault();
// Assert that the rule has been applied
Assert.Equal(apple, bananas.MyApple);
}
// This one tests the second rule, and fails
[Fact]
public void TestInsertWithExtension()
{
//Load rules
var repository = new RuleRepository();
repository.Load(x => x
.From(Assembly.GetExecutingAssembly())
.Where(rule => rule.Name.EndsWith("TestRuleWithExtension")));
//Compile rules
var factory = repository.Compile();
//Create a working session
var session = factory.CreateSession();
//Load domain model
var apple = new Apple();
//Insert facts into rules engine's memory
session.Insert(apple);
//Start match/resolve/act cycle
session.Fire();
// Query for inserted facts
var bananas = session.Query<Basket>().FirstOrDefault();
// Assert that the rule has been applied
Assert.Equal(apple, bananas.MyApple);
}
}
}
The question is why does the second rule with the DSL extension not work properly? Am I doing something wrong and how can I fix it?