Click here to Skip to main content
15,898,732 members
Articles / Programming Languages / C#

RuleSharp - C# Rule Engine/Rule Runner Library

Rate me:
Please Sign up or sign in to vote.
4.79/5 (19 votes)
6 Dec 2016CPOL4 min read 38.1K   1.1K   32   8
A library that evaluates C# conditional string expressions dynamically and returns a Boolean result

Introduction

There are scenarios where it is useful to dynamically execute a C# string expression. For example, suppose you are building a rule engine. You feed some inputs into the engine. The engine retrieves a predefined set of rules stored somewhere such as a database. It then compares your input to the set of rules and returns a Boolean result indicating whether or not the input passes the rule, based on which you execute some logic in your application.

Sometimes, you may run into situations where you find it difficult to break down all your rules into a table-like configuration to be stored in a database, etc., because the condition to be evaluated might be quite arbitrary. You might wish if only you could store a portion of your rule as a C# expression in the database and evaluate it at runtime, and based on its result, execute the rest of the application logic. This library, RuleSharp, enables you to do exactly the same thing.

Background

C# is a statically typed, compiled language. For this reason, dynamic execution of source code is not possible. Recently, Microsoft introduced a new concept called compiler as a service, which they named Roslyn. Roslyn is available only in .NET Framework 4.6 and above, but any applications targeting an earlier version of the framework cannot take advantage of Roslyn. Luckily, there is a project called Mono.CSharp, which you can use with earlier versions of .NET Framework, which offers compiler as a service. Mono.CSharp is part of the Mono project. Mono.CSharp is available on Nuget here. If you want to learn more about the Mono itself, visit this link.

RuleSharp is a wrapper library I wrote around Mono.CSharp. The library is especially useful when you build a rule engine, where you want to execute at runtime a piece of uncompiled C# code stored somewhere such as a database. All you have to do is feed an input object (a custom class) and a C# expression as string into the library. The expression represents a rule, which can be either a single-line C# statement or multiple statements. The library will try to execute the code upon the input object and return a true or false. True indicates that the rule is passed.

Using the Code

The source code of the solution is attached. The solution contains a class library called RuleSharp.RuleEngine, which is the library itself. The library has a single class named RuleRunner. RuleRunner class hosts the following methods:

C#
bool IsStatementTrue<TInput>(string statement, TInput input);
bool IsBlockTrue<TInput>(string block, TInput input); 

The difference between them is that the IsStatementTrue method expects a single-line C# expression, whereas the IsBlockTrue method can take multiple statements followed by a return; statement. Both methods take a custom object as the second input parameter.

To use the library in your application, add a reference to the library and create an instance of the RuleRunner class. The following example demonstrates how the methods can be used. Please refer to the TestConsoleApp in the same solution for a complete example.

C#
var runner = new RuleRunner();

var statementResult = runner.IsStatementTrue("input.Id == 1", employee);
var blockResult = runner.IsBlockTrue(@"if(input.Designation.Id == 1)" +
                                       @"return true;" +
                                     @"else " +
                                       @"return false;", employee);

In the above example, we pass in an input object (employee) into the methods. We also pass a string expression (also called a rule or statement). The word "input" in the expression represents the input object (in our case, the employee object). What the methods do is evaluate the expression using the employee object as the data source and return a Boolean result indicating the rule/statement success/failure.

Caveats and Points of Interest

  1. When the rule is examined, the library internally looks for the word "input" as the alias for the input object. So, it is mandatory that you use the word "input" in the string expression to represent your input object. In the above example, "input" represents the employee object. If I had written the rule as "employee.Id == 1", it would have failed to parse the rule.
  2. You should pass in a POCO object. Arrays, enumerables, enums, types are not supported as the outer object. This is because the parser is unable to correctly figure out the type of such objects. If you want to pass an array, enumerable, etc., wrap them in a custom class and pass in.
  3. If you use any types defined in a third assembly other than the calling or called ones, you must tell the library to add references to such assemblies. To do this, the RuleRunner constructor supports adding assembly references and namespace references. Again, look at the example solution attached along with this article for reference.

History

  • 6th December, 2016: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
India India
Nejimon CR started coding in early 2000s with classic Visual Basic 6 and later moved to .NET platform. His primary technology stack expertise is around Microsoft technologies, but with the previous and latest areas of functioning to include a wide variety of technologies such as Win32 APIs, AutoIt scripting, UI Automation, ASP.NET MVC and Web API, Node.js, NoSQL, Linq, Entity Framework, AngularJS, etc.

His Articles on CodeProject:
http://www.codeproject.com/Articles/1060520/Centralizing-WCF-Client-Configuration-in-a-Class-L
http://www.codeproject.com/Articles/567356/Asynchronous-Access-of-Web-Service-from-WPF-with-B
http://www.codeproject.com/Articles/63849/Serial-Foot-Pedal-Device-Server
http://www.codeproject.com/Tips/149249/Simplest-way-to-implement-irregular-forms-in-NET
http://www.codeproject.com/Tips/564388/Entity-Framework-Code-First-Navigation-Property-is

Comments and Discussions

 
QuestionSimple and useful library. Is it possible to have more than one input classes? Pin
Member 175040625-Jun-18 6:22
Member 175040625-Jun-18 6:22 
SuggestionProject name is a duplicate Pin
Member 80650516-Feb-17 10:13
Member 80650516-Feb-17 10:13 
QuestionGreat Pin
DavidLoup7-Dec-16 3:56
DavidLoup7-Dec-16 3:56 
AnswerRe: Great Pin
Nejimon CR8-Dec-16 22:37
Nejimon CR8-Dec-16 22:37 
QuestionPrimitive Rule Engine, but... Pin
Dewey6-Dec-16 7:09
Dewey6-Dec-16 7:09 
AnswerRe: Primitive Rule Engine, but... Pin
Nejimon CR8-Dec-16 22:39
Nejimon CR8-Dec-16 22:39 
QuestionSomething tha I have been looking for Pin
Jayaraj Peediyakal5-Dec-16 17:38
Jayaraj Peediyakal5-Dec-16 17:38 
AnswerRe: Something tha I have been looking for Pin
Nejimon CR6-Dec-16 0:05
Nejimon CR6-Dec-16 0:05 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.