Building a rule engine in c# (part 5: bringing it all together)

In the first four post of these series i showed a lot of code parts. I became some mails about that posts that show me that my descriptions are not as good as i thought. For that reason i decided to write that fifth post to bring it all together. First of all i decided to bring the source code of the posts together and so i created the ruleengine project at ruleengine.codeplex.com. Here are the links to the four previous posts and to the description of the expression evaluator.

Now i want to describe the tests i added to the ruleengine.codeplex.com project to show how to use ist. First i want to show the Person and Adress classed i use to validate the rules against:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public int Children { get; set; }
    public bool Married { get; set; }
    public Adresse Adresse_ { get; set; }
    private List<Adresse> adresses = new List<Adresse>();
    public List<Adresse> Adresses_ 
    { 
        get { return adresses; } 
        set { adresses = value; } 
    }
}

The Person class has some properties (Name, Age, Children, Married) and a collection of Adresses.

public class Adresse
{
    public string Street { get; set; }
    public int Plz { get; set; }
    public string City { get; set; }
    public bool ActiveState { get; set; }
}

The Adress class has some simple properties (Stree, Plz, City, ActiveState).
I will start with a simple test case to test a persons properties against a specific rule.

[TestMethod]
public void SimpleRuleLoaderPersonTest()
{
    Person person = new Person() { Name = "Mathias", Age = 36, Children = 2, Married = true };
    RuleLoader ruleLoader = new RuleLoader();
    // new Rule("Age", Operator.LessThanOrEqual, 50);
    Rule rule = ruleLoader.Load(2);
    RuleEngine.RuleEngine ruleEngine = new RuleEngine.RuleEngine();
    var ruleFunc = ruleEngine.CompileRule<Person>(rule);
    var result = ruleFunc(person);
    Assert.AreEqual(result, true);
}

That test defines a Person object and instanciates a RuleLoader object. That ruleLoader object would load the rules from the database but in my special case i wanted to use no database so the rule Loader is just a big switch statement that creates rules:

public class RuleLoader
{
    public Rule Load(int id)
    {            
        switch(id)
        {
            case 1 : 
                return new Rule("Name", Operator.NotEqual, "test");
            case 2 : 
                return new Rule("Age", Operator.LessThanOrEqual, 50);
            case 3: 
                return new Rule("Children", Operator.GreaterThan, 0);
            case 4: 
                return new Rule("City", Operator.Equal, "New York");
            case 5: return 
                new Rule("ActiveState", Operator.Equal, true);                
            case 6: 
                return new Rule("DecimalValue", Operator.GreaterThanOrEqual, 1);
            case 7: 
                return new Rule("DecimalValue", Operator.GreaterThanOrEqual, 1);
            case 8: 
                return new Rule("Married", Operator.Equal, true);
            default :
                return null;
        }
    }        
}

After that i use the RuleEngine to build a Func delegate out of the defined rule. In this case it is a Func of Person, bool delegate. This delegate gets invoked with the Person object as parameter and returns true or false. The second test shows how to use the RuleValidator class. That class has defined different ValidatorRule methodes to validate objects against rules.

[TestMethod]
public void SimpleRuleLoaderPersonValidateRulesAny()
{
    Person person1 = new Person() { 
        Name = "Mathias", Age = 36, Children = 2, Married = true };
    Person person2 = new Person() { 
        Name = "Anna", Age = 32, Children = 2, Married = false };
    RuleLoader ruleLoader = new RuleLoader();
    // new Rule("Age", Operator.LessThanOrEqual, 50);
    Rule rule1 = ruleLoader.Load(2);
    // new Rule("Children", Operator.GreaterThan, 0);
    Rule rule2 = ruleLoader.Load(3);
    RuleEngine.RuleEngine ruleEngine = new RuleEngine.RuleEngine();
    var ruleFuncs = ruleEngine.CombineRules<Person>(
        new Rule[] { rule1, rule2 });
    RuleValidator ruleValidator = new RuleValidator();
    var result = ruleValidator.ValidateRulesAny(
        new[] { person1, person2 }, ruleFuncs);
    Assert.AreEqual(result, true);
}

In this case we use the CombineRules method of the RuleEngine to create an array of Func delegates. That delegates and the two Person objects are overtaken to the ValidateRulesAny method of the RuleValidator. That method returns true if every person fulfills at least one rule. The next test shows how to use the RuleEngine to validate collections of objects against rules.

[TestMethod]
public void SimpleRuleLoaderPersonAddressValidateRulesAll()
{
    Person person = new Person() { 
        Name = "Mathias", Age = 36, Children = 2, Married = true };
    Adresse adresseOne = new Adresse() { 
        Street = "Teststreet1", Plz = 3030, City = "New York", ActiveState = true };
    Adresse adresseTwo = new Adresse() { 
        Street = "Teststreet2", Plz = 1010, City = "London", ActiveState = false };
    Adresse adresseThree = new Adresse() { 
        Street = "Teststreet3", Plz = 2020, City = "Paris", ActiveState = false };        
    person.Adresses_.Add(adresseOne);
    person.Adresses_.Add(adresseTwo);
    person.Adresses_.Add(adresseThree);
    RuleLoader ruleLoader = new RuleLoader();
    // new Rule("City", Operator.Equal, "New York");
    Rule firstAdressRule = ruleLoader.Load(4);
    // new Rule("ActiveState", Operator.Equal, true);      
    Rule secondAdressRule = ruleLoader.Load(5);
    RuleEngine.RuleEngine ruleEngine = new RuleEngine.RuleEngine();
    var firstAdressRuleFunc = 
        ruleEngine.CompileRule<Adresse>(firstAdressRule);
    var secondAdressRuleFunc = 
        ruleEngine.CompileRule<Adresse>(secondAdressRule);
    RuleValidator ruleValidator = new RuleValidator();
    bool result = ruleValidator.
        ValidateValuesAny(person.Adresses_, firstAdressRuleFunc);
    Assert.AreEqual(result, true);
    result = ruleValidator.
        ValidateValuesAny(person.Adresses_, secondAdressRuleFunc);            
    Assert.AreEqual(result, true);
}

Here the Person objects owns a List of Adress objects. The RuleValidator class has some methods that take a List and validates every element of that list against a rule.(ruleValidator.ValidateValuesAny(person.Adresses_, firstAdressRuleFunc) ). The next test shows how the ruleEngine sums up a property and validates this sum against two rules.

[TestMethod]
public void SimpleRuleLoaderPersonValidateRulesSum()
{
    Person person1 = new Person() { 
        Name = "Mathias", Age = 35, Children = 2 };
    Person person2 = new Person() { 
        Name = "Anna", Age = 32, Children = 2 };
    RuleLoader ruleLoader = new RuleLoader();
    // new Rule("Age", Operator.LessThanOrEqual, 50);
    Rule rule1 = ruleLoader.Load(2);
    // new Rule("Children", Operator.GreaterThan, 0);
    Rule rule2 = ruleLoader.Load(3);
    RuleValidator ruleValidator = new RuleValidator();
    var result = ruleValidator.ValidateRulesSum(
        new Person[] { person1, person2 },
        new Rule[] { rule1, rule2 });            
    Assert.AreEqual(result, false);
}

Here is use the ValidateRulesSum method. This method takes an array of objects (person1, person2) and an array of rules (rule1, rule2). It takes the first rule checks what property of the Person object this rule uses, sums up that property value of all overtaken objects and checks that sum against the rule. It returns true if the sum of all properties pass all rules. The next test shows the usage of the expression evaluator rules. In this case the Rules are expression rules ( like ” Name = ‘mathias’ “).

[TestMethod]
public void SimpleRuleLoaderPersonEvaluateExpression()
{
    Person person1 = new Person() 
        { Name = "Mathias", Age = 35, Children = 2 };
    ExpressionRuleLoader expressionRuleLoader = new ExpressionRuleLoader();
    // new Rule(" Name = 'mathias' ");
    Rule rule1 = expressionRuleLoader.Load(1);
    // new Rule(" Age = 35 ");
    Rule rule2 = expressionRuleLoader.Load(2);           
    RuleValidator ruleValidator = new RuleValidator();
    var result = ruleValidator.ValidateExpressionRules(person1, rule1);
    Assert.AreEqual(result, true);
    result = ruleValidator.ValidateExpressionRules(person1, rule2);
    Assert.AreEqual(result, true);
}

The RuleValidator class has methodes (e.g. ValidateExpressionRules) to validate the Expression Rules against objects. In this case the rules are ” Name = ‘Mathias’ ” and ” Age = 35 “. To look what the expression evaluator can afford and what not please look at my the implementing expression evaluator in c# post.
The source code of the expression evaluator is part of the ruleengine.codeplex.com project and can also be downloaded from simpleexpeval.codeplex.com.