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.

Advertisements


If you have a note or a question please write a comment.

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s