Building a rule engine in c# (part 2: extending the rule engine to handle collections)

In my blog post about building a rule engine in c# i wrote about the core implementation of a rule engine. In this seconde post i will extend the rule engine so that it can handle collections of rules and collections of items to validate against the rules. To do so i have to extend the RuleEngine class and i have to introduce a RuleValidator class. With the extension of the RuleEngine the following code is possible:

ProcessingEngineDatabase

RuleLoader ruleLoader = new RuleLoader();
Rule firstRule = ruleLoader.Load(1);
Rule secondRule = ruleLoader.Load(2);
Rule thirdRule = ruleLoader.Load(3);

RuleEngine ruleEngine = new RuleEngine();
var ruleFuncs = ruleEngine.CombineRules<Person>(
    new Rule[] { firstRule, secondRule, thirdRule });

That means I load first, second and third rule and combine them to a array of rules. To do so i implemented the CombineRules method in the RuleEngine class.

public Func<T, bool>[] CombineRules<T>(Rule[] rules)
{
    List<Func<T, bool>> list = new List<Func<T, bool>>();
    foreach (Rule rule in rules)
    {
        if (string.IsNullOrEmpty(rule.PropertyName))
        {
            ExpressionBuilder expressionBuilder = new ExpressionBuilder();
            var param = Expression.Parameter(typeof(T));
            Expression expression = expressionBuilder.BuildExpression<T>(rule.Operator_, rule.Value, param);
            Func<T, bool> func = Expression.Lambda<Func<T, bool>>(expression, param).Compile();
            list.Add(func);
        }
        else
        {
            ExpressionBuilder expressionBuilder = new ExpressionBuilder();
            var param = Expression.Parameter(typeof(T));
            Expression expression = expressionBuilder.BuildExpression<T>(rule.PropertyName, rule.Operator_, rule.Value, param);
            Func<T, bool> func = Expression.Lambda<Func<T, bool>>(expression, param).Compile();
            list.Add(func);
        }
    }
    return list.ToArray();
}

The i use the new RuleValidator to validate one or a collection of objects against that defined rules.

RuleValidator ruleValidator = new RuleValidator();
bool result = ruleValidator.ValidateRulesAll(person, ruleFuncs);
result = ruleValidator.ValidateRulesAny(person, ruleFuncs);

As we see there are two methodes to validate the person object against the rules. The ValidateRulesAll method returns true if all rules are passed by the overtaken person object. The ValidateRulesAny method return true if any rule is passed by the overtaken person object. It is also possible to validate not only one person object but a collection of person objects.

Person person1 = new Person() { Name = "Mathias", Age = 36, Children = 2 };
Person person2 = new Person() { Name = "Anna", Age = 33, Children = 2 };
bool result = ruleValidator.ValidateRulesAll(new Person[] { person1, person2 }, ruleFuncs);
result = ruleValidator.ValidateRulesAny(new Person[] { person1, person2 }, ruleFuncs);

In this case both methodes (ValidateRulesAll and ValidateRulesAny) return true because the test persons both pass all three rules. The implementation of the RuleValidator class is very simple. It is just a foreach loop that calls the overtaken rules with the overtaken values. In the ValidateRulesAll method it checks if all rules are passed and then returns true, in the ValidateRulesAny it checks one rule after the other and if one rule is passed it returns true.

public class RuleValidator
{
    public bool ValidateRulesAll<T>(T value, Func<T, bool>[] rules)
    {
        foreach (var rule in rules)
        {
            if (!rule(value))
                return false;
        }
        return true;
    }

    public bool ValidateRulesAny<T>(T value, Func<T, bool>[] rules)
    {
        foreach (var rule in rules)
        {
            if (rule(value))
                return true;
        }
        return false;
    }

    public bool ValidateRulesAll<T>(T[] values, Func<T, bool>[] rules)
    {
        foreach (var value in values)
        {
            foreach (var rule in rules)
            {
                if (!rule(value))
                    return false;
            }
        }
        return true;
    }

    public bool ValidateRulesAny<T>(T[] values, Func<T, bool>[] rules)
    {
        foreach (var value in values)
        {
            bool validated = false;
            foreach (var rule in rules)
            {
                if (rule(value))
                {
                    validated = true;
                    break;
                }
            }
            if (!validated)
                return false;
        }
        return true;
    }
}

In this third part of the post series called Building a rule engine in c# (part 3: extending the rule engine to handle aggregations of collections) i extend the rule engine to handle aggregation for collections. In the fourth part of this blog series i write about implementing an expression evaluator to define more complex expressions Building a rule engine in c# (part 4: extending the rule engine to evaluate defined expressions) and in the fifth part building a rule engine in c# (part 5: bringing it all together) i give an overview about the usage of the ruleengine.codeplex.com project i created to bring the code parts in that post series together.


Building a rule engine in c#

In my current project in work i needed a simple rule engine that processes rules that are saved in the database. In the Database i have the following table:

The column Property stores the name of the property of the overtaken class. The column Operator stores the logical operator which is used to evaluate the rule and the column Value stores the constant to evaluate against. To try this rules we have the Person class.

    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public int Children { get; set; }
    }

This class has the properties Name, Age and Children. The rule class stores the rules that are defiened in the database table.

    public class Rule
    {
        private bool propertySet = false;
        public string PropertyName { get; set; }
        public Operator Operator_ { get; set; }
        public object Value { get; set; }

        public Rule(Operator operator_, object value)
        {
            this.Operator_ = operator_;
            this.Value = value;
        }

        public Rule(string propertyName, Operator operator_, object value)
        {
            this.Operator_ = operator_;
            this.Value = value;
            this.PropertyName = propertyName;
            if(!string.IsNullOrEmpty(propertyName))
                this.propertySet = true;
        }
    }

The RuleLoader class loads the rules from the database and stores it in Rule objects. This is the code to load the rules from the database.

            RuleLoader ruleLoader = new RuleLoader();
            Rule firstRule = ruleLoader.Load(1);
            Rule secondRule = ruleLoader.Load(2);
            Rule thirdRule = ruleLoader.Load(3);

Now we have the rules and what we need is a Person to apply the rules to.

Person person = new Person() { Name = "Mathias", Age = 35, Children = 2 };

Now we will define the rule engine that evaluates the values of the property of the Person objects against the definied rules from the database. To do so we need some reflection and some objects from the System.Linq.Expressions namespace. This is the RuleEngine class that generates a Func that we use to evaluate the overtaken rule.

    public class RuleEngine
    {
        public Func<T, bool> CompileRule<T>(Rule rule)
        {
            if (string.IsNullOrEmpty(rule.PropertyName))
            {
                ExpressionBuilder expressionBuilder = new ExpressionBuilder();
                var param = Expression.Parameter(typeof(T));
                Expression expression = 
                    expressionBuilder.BuildExpression<T>(rule.Operator_, rule.Value, param);
                Func<T, bool> func = 
                    Expression.Lambda<Func<T, bool>>(expression, param).Compile();
                return func;
            }
            else
            {
                ExpressionBuilder expressionBuilder = new ExpressionBuilder();
                var param = Expression.Parameter(typeof(T));
                Expression expression = 
                    expressionBuilder.BuildExpression<T>(
                    rule.PropertyName, rule.Operator_, rule.Value, param);
                Func<T, bool> func = 
                    Expression.Lambda<Func<T, bool>>(expression, param).Compile();
                return func;
            }
        }
    }

The RuleEngine class uses the ExpressionBuilder class to build the Expression and compiles it to a lambda expression. This lambda expression does the actual work when a rule is evaluated.

    public class ExpressionBuilder
    {
        public Expression BuildExpression<T>(
            Operator ruleOperator, object value, ParameterExpression parameterExpression)
        {
            ExpressionType expressionType = new ExpressionType();
            var leftOperand = parameterExpression;
            var rightOperand = 
                Expression.Constant(Convert.ChangeType(value, typeof(T)));
            var expressionTypeValue = 
                (ExpressionType)expressionType.GetType().GetField(
                Enum.GetName(typeof(Operator), ruleOperator)).GetValue(ruleOperator);
            var binaryExpression = 
                Expression.MakeBinary(expressionTypeValue, leftOperand, rightOperand);
            return binaryExpression;
        }

        public Expression BuildExpression<T>(
            string propertyName, Operator ruleOperator, object value, 
            ParameterExpression parameterExpression)
        {
            ExpressionType expressionType = new ExpressionType();
            var leftOperand = MemberExpression.Property(parameterExpression, propertyName);
            var rightOperand = Expression.Constant(Convert.ChangeType(value, value.GetType()));
            FieldInfo fieldInfo = 
                expressionType.GetType().GetField(Enum.GetName(typeof(Operator), ruleOperator));
            var expressionTypeValue = (ExpressionType)fieldInfo.GetValue(ruleOperator);
            var binaryExpression = 
                Expression.MakeBinary(expressionTypeValue, leftOperand, rightOperand);
            return binaryExpression;
        }
    }

Here we see the usage of the RuleEngine class. We call the CompileResult method and get back a Func that evaluates us the overtaken parameter (in this case of type Person).

     RuleEngine ruleEngine = new RuleEngine();
     var firstRuleFunc = ruleEngine.CompileRule<Person>(firstRule);
     var secondRuleFunc = ruleEngine.CompileRule<Person>(secondRule);
     var thirdRuleFunc = ruleEngine.CompileRule<Person>(thirdRule);
     var result = firstRuleFunc(person) &&
         secondRuleFunc(person) && thirdRuleFunc(person);

So we know that the Person object

( Person person = new Person() { Name = "Mathias", Age = 35, Children = 2 }; )

passes all the defiend rules from the database.

In the second part of this post i will extend the rule engine to handle collections:
Building a rule engine in c# (part 2: extending the rule engine to handle collections) In this third part of the post series called Building a rule engine in c# (part 3: extending the rule engine to handle aggregations of collections) i extend the rule engine to handle aggregation for collections. In the fourth part of this blog series i write about implementing an expression evaluator to define more complex expressions Building a rule engine in c# (part 4: extending the rule engine to evaluate defined expressions) and in the fifth part building a rule engine in c# (part 5: bringing it all together) i give an overview about the usage of the ruleengine.codeplex.com project i created to bring the code parts in that post series together.