One obvious solution is to use reflection.
Here we will show a solution which uses Expression trees tocompile the rules into fast executable delegates. We can then evaluate the rules as if they were normal boolean functions.

Now we can implement the rule validation by compling the rules and then simply invoking them:

var rule = newRule ("Age", "GreaterThan", "20");

Func<User, bool> compiledRule = CompileRule<User>(rule);

// true if someUser.Age > 20

bool isMatch = compiledRule(someUser);

Of course we compile all the rules just once and then use the compiled delegates:

// Compile all the rules once.

var compiledRules = rules.Select(r => CompileRule<User>(r)).ToList();

// Returns true if user satisfies all rules.

publicbool MatchesAllRules(User user)

{

return compiledRules.All(rule => rule(user));

}

Note that the “compiler” is so simple because we are using 'GreaterThan' in the rule definition, and 'GreaterThan' is a known .NET name for the operator, so the string can be directly parsed. The same goes for 'Contains' – it is a method of List. If we need custom names we can build a very simple dictionary that just translates all operators before compiling the rules:

Dictionary<string, string> nameMap = newDictionary<string, string> {

{ "greater_than", "GreaterThan" },

{ "hasAtLeastOne", "Contains" }

};

That’s it. As an improvement we could add error messages to the rule definitions and print the error messages for the unsatisfied rules.

64
comments:

Hey, great stuff!I never really gotten into Expression Trees but it seems that I must, since they're so powerful.I'd suggest using an Enum for the operator List rather than a Dictionary, with the obvious commitment of not having a dynamic List of operators, if that is a requirement, the Dictionary is the correct and obvious choice :)

Hi, why in the expression "ExpressionType.TryParse" tells me that cannot resolve symbol tryparse?It seems like ExpressionType has no TryParse method...and it should nat because ExpressionType is an enum type, isn't it?

Hi Martin,thanks for this great article.Currently I'm developping a tool that take as input a list of rules (in db),and based on the result of tthe rule(if it's true) it takes an action et optionally raise a warning/error/message. An action can lead to an another rule.The answer for the first part of my question is already in this article.Still,I would like to know how to take Action based on the result of a rule and how to optionally raise event (Alert/Warning/Message) ?Thank you.

As for the second part about matching rules leading to additional rules: a simple solution would be to also give each rule a Name and store it in the database. Also with every rule, you would store a list of rule names that should be also evaluated when the rule matches.

Just be careful about cyclic references between rules, so that evaluating a rule does not lead to a stack overflow.

Also note that all this work of storing rules in the database is only useful if you actually want your users to define the rules. If the rules are fixed, then you are better off encoding them in your code yourself.

Hi Martin,Thank you very much for the great answer !Unfortunately life is not so easy :) :

If the PropertyMember is a list of item (say generic list of options L = List)IndexOption has an enum property.I we want to check that an IndexOption exist and is equal to a targetvalue (ie:check that ONLINE option exist and is equal to OFF),Could you figure out how to do this ?Thanks

Interesting! i've been pulling my hair out the last two weeks trying to find a way to implement rule engines without having to get a PhD in design patterns. Thanks for the article.

I am, however, having problems compiling the solution, the following error keeps popping up:

System.Workflow.Activities.Rules.Rule' does not contain a definition for 'MemberName' and no extension method 'MemberName' accepting a first argument of type 'System.Workflow.Activities.Rules.Rule'

and

'System.Workflow.Activities.Rules.Rule' does not contain a definition for 'Operator' and no extension method 'Operator' accepting a first argument of type 'System.Workflow.Activities.Rules.Rule' could be found (are you missing a using directive or an assembly reference?)

Also It seems that There is no Rule Constructor with only three arguments available

this post is quite a simple example unrelated to the Workflow foundation. I'm not very familiar with Workflow foundation but a google search for "workflow foundation rule engine" gave me some interesting results.

Hi Martin Konicek, Could you please assist. I'm interest in understanding the above code as I'm interested in building a rule engine. I've used the above code but when I execute my test solution, I get an error in the "BuildExpression" function. See error message "The value passed in must be an enum base or an underlying type for an enum, such as an Int32. Parameter name: value". Am I missing something here. Thanks for you help. Lindsay

Hi Martin, thank for responding. Much appreciated. The basic implementation is in order. I just need to get a feel for the new technology as I spend most of my days coding in sql and I'm looking to implement rule based application. Regards Lindsay

This is definitely a good exercise for the Expression trees API. However, since DataRows are already dynamic (the compiled code will still do column lookups by name at runtime), I was thinking maybe expression trees won't give you that much of a performance benefit in this case. You would have to measure of course. You would still get the speedup of the '== 25' or '.Contains("foo")' being done without any IFs or reflection at runtime, but still.. a few switch statements plus some reflection might be easier. The expression trees are purely a performance optimization.

Thanks very much Martin. I would try this and let you know. I am basically trying to design a Rule Engine where the user can define rules on a .net dataset and tables within it. Basically the user would see columns of a table and decide what 'rules' they want to apply. For Example, IsNull, IsNumeric, IsISOFormat, IsInList, Equals, GreaterThan etc.

I want to build expression trees for the rules and then store them in a database as a template. Then I have service that will read this expression from the database and apply it to the data the service is currently processing.

Is this the right approach or do you know of a better and easier approach. As you can see I won't be just using the .Net's operators but would be calling functions/delegates for my specialised operators. For example, IsInList would contain a name of the list that would be somewhere in the database. When a column value is to be checked In a list using IsInList - it will essentially call a function to check that the value in the column exists in the named list passed as IsInList operator. Then I would have more complicated rules. For example checking Value in table1.column1 exists in table2.Column3 and Is not null and IsInList etc.

I thought of using CodeDom but it seemed really complicated. I also had a look at the Windows Workflow Foundation's Rules but they also seemed really cumbersome for what I am trying to do.

I read somewhere that the expression trees are very powerful and usually more optimized than other technique.

If you think this is not the right approach then I would really appreciate if you could direct me to better alternatives.

Hi Martin, is there a way to see if the Expression with specified operator (ExpressionType) is valid or do i have to implement it by my self. For example GreaterThan with two string parameters throws exception when expression is compiled. Ivan

Great article thanks.Not being an experienced ("sharp") C#er, I wonder if the comment / suggestion below is valid.Seems to me that by compiling the rule the operations and values (e.g. u.Age, "Equals", "20") get 'baked-in'.

If so, the technique could be much more powerful if these could be parameterized - if I understand the technique correctly - at the price of having to construct / compile the rule upon each invocation.

Any feedback or suggestion for implementing such idea is appreciated - even if my question / comment reveal total ignorance.

Thanks for the clean design / thought process.

Am looking for the posted working code sample (my interest lies mainly in using this model for implementation of a QA Automation tool - the object(s) against which the rules will be invoked will be mainly UITestControl instances.)

and adapt BuildExpr to support this syntax. It's not trivial but definitely doable.

You might also want to support rules with paths such as "Parents[*].Parents[*].Age", but there it's not clear what the semantics should be - is it a single collection of integers, or a collection of collections of integers, and how do you match on the inner collections?

Hi, I am really struggling to get nested properties to work. I have looked at the Expression.Property method: http://msdn.microsoft.com/en-us/library/system.linq.expressions.expression.property.aspx but still can't get it to work. Any help would be much appreciated (such as a code snippet?).

Hi Martin, Thanks for the reply, I did exactly that. In fact I Split the r.MemberName on the "." and loop through building the left Expression recursively. i.e. left = MemberExpression.Property(left, leftOperands[i]);

to create an expression for "Collection[0].Code" you'll have to extend your parser that splits the string by dots to also look for integers inside square brackets. The left expression of the "Code" MemberExpression will be an IndexExpression which you can create e.g. using one of the overloads of Expression.Property: http://msdn.microsoft.com/en-us/library/dd630266.aspx

I found this by simply full-text searching the Expression API doc for "index":http://msdn.microsoft.com/en-us/library/system.linq.expressions.expression_methods.aspx

"Collection[*].Code" is more complicated. You could translate this into an expression tree equivalent to Collection.Any(item => item.Code.Contains("C137")).

Note that if you find out your parser of string containing C# expressions is getting complex, you can check out the open-source NRefactory library that contains a C# parser:http://www.codeproject.com/Articles/408663/Using-NRefactory-for-analyzing-Csharp-code

Great Article, I am planning to use Expressions in one of the projects that I am working on. I have a situation where the user will create a set of rules dynamically and I have to store them in the database. They could be such as [rule1 && (rule2 || rule3)] combunation of AND and OR. Noticed that there are solutions to have seperate and list and or list but what is the best way to approach if I have to persist the expression above.

Hi Maritn,its great article and i used this concept to build custom rule engine, and i want to use new method which is not available in Binary Expressions, similar to Contains, Say StartsWith. but i couldn't find it in the String type, so i tried to use extensions it didn't work though, any idea how to use custom method to work with system types?

Great document. I posted a case in stactoverflow about rule engine (http://stackoverflow.com/questions/33500953/how-evaluate-an-object-in-a-collection-with-microruleengine). Basically i need testing how access collections. Have you an idea of it? Thanks

Great artical, I was looking for creating some code samples to create my own business rule engine and this one is really good any easy to understand.

I am not an expert of expression tree, could you please let me know if I can use this solution for creating the rule engine which can process thousands of rule per minute. I want to from the performance perspective using Expression tree based solution will be good over the other available solution like NRules.

Right now, I'm parsing and splitting each one of the conditions and checking manually to the object properties (is a Task class). Is there any way to do it with a expression builder?I'm asking, because I've tried to adapt your BuildExpr, but the lack of knowledge is not driving me anywhere.Thanks in advance. Saludos desde Uruguay

We provide the best free cricket betting tips in worlds .Find us hereWe provide the best free cricket betting tips in worlds .Find us today cricket match predction You need not go anywhere for other cbtf tips as we provide best cricket betting tips