Detailed explanation of the use of. NET RulesEngine (rule engine)

  • 2021-11-29 23:37:28
  • OfStack

Overview of directory RulesEngine How to use Extension method used in expression tree Multi-object combination condition How is it achieved? Success and failure events Summarize

By chance, let me take out RulesEngine to complete a business. For the business, it is mainly to complete a business judgment of scalability (uncertain types and uncertain conditions, and changes in conditions may be continuously increased and modified). For example, when an achievement system is completed, the administrator can create it. For achievements, there is a sexual unlock, daily routine, weekly routine, and reset at any time, which is triggered every time it is achieved. Facing the increase of achievement tasks, it is a headache for programmers to add and modify these achievement tasks every time. Ok, we should have a simple understanding of this, so follow the author down, and let's see how to use very little code in. NET to complete a simple dynamic logic processing.

Overview of RulesEngine

RulesEngine is a rule engine project introduced by Microsoft, which is used for business logic/rules/policies abstracted from the system. In the process of our development, it is inevitable to deal with this kind of repeated business logic, and for this kind of dynamic rules, it is a more elegant way to use us to reduce the modification of our code or project.

How to use

At present, we can introduce the library in the form of nuget, as follows:


dotnet add package RulesEngine 

For the configuration of rules, we can directly pass the typed parameters. The author mainly wants us to understand clearly, so we use JSON configuration to demonstrate.


// Deserialization Json Format rule string 
var workflowRules = JsonConvert.DeserializeObject<List<WorkflowRules>>(rulesStr);
 var rulesEngine = new RulesEngine.RulesEngine(workflowRules.ToArray());

 // Define rules 
            var rulesStr = @"[{
                    ""WorkflowName"": ""UserInputWorkflow"",
                    ""Rules"": [
                      {
                        ""RuleName"": ""CheckAge"",
                        ""ErrorMessage"": "" Age must be greater than 18 Years old ."",
                        ""ErrorType"": ""Error"",
                        ""RuleExpressionType"": ""LambdaExpression"",
                        ""Expression"": ""Age > 18""
                      },
                       {
                        ""RuleName"": ""CheckIDNoIsEmpty"",
                        ""ErrorMessage"": "" ID No. cannot be blank ."",
                         ""ErrorType"": ""Error"",
                        ""RuleExpressionType"": ""LambdaExpression"",
                        ""Expression"": ""IdNo != null""
                      }
                    ]
                  }] ";

As shown above, we have defined the rule information. For this information, JSON data is stored by default for the rule information. Of course, you can store the following contents and split the following data structures into the database.

属性 描述
RuleName 规则名称
Properties 规则属性,获取或设置规则的自定义属性或者标记
Operator 操作符
ErrorMessage 错误消息
Enabled 获取和设置规则是否已启用
RuleExpressionType 规则表达式类型,默认为LambdaExpression,当然目前只有这么1个
WorkflowRulesToInJect 注入工作流程规则
Rules 规则
LocalParams 本地参数
Expression 表达树
Actions
SuccessEvent 完成事件,默认为规则名称

Let's look at the result of this code in 1. For this content, the author created a class, as follows:


   public class UserInput
        {
            public string IdNo { get; set; }
            public int Age { get; set; }
        }

static async Task Main(string[] args)
        {
            var userInput = new UserInput
            {
                IdNo = null,
                Age = 18
            };

            // Deserialization Json Format rule string 
            var workflowRules = JsonConvert.DeserializeObject<List<WorkflowRules>>(rulesStr);
            
            var rulesEngine = new RulesEngine.RulesEngine(workflowRules.ToArray());

            List<RuleResultTree> resultList = await rulesEngine.ExecuteAllRulesAsync("UserInputWorkflow", userInput);
            foreach (var item in resultList)
            {               
                 Console.WriteLine(" Verification succeeded: {0} , message: {1}",item.IsSuccess,item.ExceptionMessage);
            }

            Console.ReadLine();

        }

The output is as follows:

Verification successful: False, message: Age must be greater than 18 years old.
Verification succeeded: False, message: ID number cannot be empty.

Return structure resultList As shown below:


 { "Rule":{ "RuleName":"CheckNestedSimpleProp","Properties":null,"Operator":null,"ErrorMessage":" Age must be greater than 18 Years old .",
                "ErrorType":"Error","RuleExpressionType":"LambdaExpression","WorkflowRulesToInject":null,"Rules":null,"LocalParams":null,"Expression":"Age > 18","Actions":null,"SuccessEvent":null},"IsSuccess":false,"ChildResults":null,"Inputs":{ "input1":{ "IdNo":null,"Age":18} },
                "ActionResult":{ "Output":null,"Exception":null},"ExceptionMessage":" Age must be greater than 18 Years old .","RuleEvaluatedParams":[]}

Extension method used in expression tree

I believe you have a simple understanding of the use of the rule engine above, and let's come to an advanced version of the content below.

For example, I think the age entered is inaccurate. I want to calculate the age by the ID number, so how should I operate? Under normal circumstances, we will pass the ID number parameter to the handler through the expansion method. After the handler calculates, it will return to our age. How should we operate in this? Let's look down.

Adding custom types through ReSettings will extend methods, because the methods they can use are limited to [System namespace], so we need to add custom classes to the settings.


   private static readonly ReSettings reSettings = new ReSettings
        {
            CustomTypes = new[] { typeof(IdCardUtil) }
        };

Amend the following:


var rulesEngine = new RulesEngine.RulesEngine(workflowRules.ToArray(), null, reSettings: reSettings);

var rulesStr = @"[{
                    ""WorkflowName"": ""UserInputWorkflow"",
                    ""Rules"": [
                      {
                        ""RuleName"": ""CheckNestedSimpleProp"",
                        ""ErrorMessage"": "" Age must be less than 18 Years old ."",
                        ""ErrorType"": ""Error"",
                        ""RuleExpressionType"": ""LambdaExpression"",
                        ""Expression"": ""IdNo.GetAgeByIdCard() < 18""
                      },
                       {
                        ""RuleName"": ""CheckNestedSimpleProp1"",
                        ""ErrorMessage"": "" ID No. cannot be blank ."",
                         ""ErrorType"": ""Error"",
                        ""RuleExpressionType"": ""LambdaExpression"",
                        ""Expression"": ""IdNo != null""
                      }
                    ]
                  }] ";

The output is as follows:

Verification successful: False, message: age must be less than 18 years old.
Verification succeeded: True, message:

Multi-object combination condition

Below, we modified the previous rule content under 1, and added a class ListItem at the same time. After assigning the content, we created an anonymous type with two attributes, user and items, and finally made logical judgment through our multi-condition combination.


            var rulesStr = @"[{
                    ""WorkflowName"": ""UserInputWorkflow"",
                    ""Rules"": [
                      {
                        ""RuleName"": ""CheckNestedSimpleProp"",
                        ""ErrorMessage"": ""Value Value is not second."",
                        ""ErrorType"": ""Error"",
                        ""RuleExpressionType"": ""LambdaExpression"",
                        ""Expression"": ""user.UserId==1 && items[0].Value==second""
                      }
                    ]
                  }] ";


            var userInput = new UserInput
            {
                UserId = 1,
                IdNo = "11010519491230002X",
                Age = 18
            };
            var input = new
            {
                user = userInput,
                items = new List<ListItem>()
                {
                    new ListItem{ Id=1,Value="first"},
                    new ListItem{ Id=2,Value="second"}
                }
            };

The output is as follows:

Verification succeeded: False, message: Value value is not second.

How is it achieved?

For this, we should look at the principle according to the phenomenon. For the internal dynamic tree, System. Linq. Dynamic. Core is actually used. RulesEngine is based on this library and abstracted, providing us with a rule engine. Let's try System. Linq. Dynamic. Core.

We first query the collection data and edit a condition string, as follows:


// Deserialization Json Format rule string 
var workflowRules = JsonConvert.DeserializeObject<List<WorkflowRules>>(rulesStr);
 var rulesEngine = new RulesEngine.RulesEngine(workflowRules.ToArray());
0

Output:

Id: 1, Value: first

Then let's look at how we do it if we use the expression tree, as follows:


// Deserialization Json Format rule string 
var workflowRules = JsonConvert.DeserializeObject<List<WorkflowRules>>(rulesStr);
 var rulesEngine = new RulesEngine.RulesEngine(workflowRules.ToArray());
1

Normally, it is like this. We splice the conditions. I believe everyone can think about one condition and determine what is suitable for themselves.

If you use a dynamic query form as follows:


// Deserialization Json Format rule string 
var workflowRules = JsonConvert.DeserializeObject<List<WorkflowRules>>(rulesStr);
 var rulesEngine = new RulesEngine.RulesEngine(workflowRules.ToArray());
2

Success and failure events

Because for logical verification, since we want to do this, we definitely need to know whether it succeeded or failed. And we can get the failure and success of logical verification not only through the IsSuccess of the object, but also through two events, as follows:


            var discountOffered = "";

            resultList.OnSuccess((eventName) =>
            {
                discountOffered = $" Success event :{eventName}.";
            });


            resultList.OnFail(() =>
            {
                discountOffered = " Failure event .";
            });

Summarize

If you are interested, you can see System. Linq. Dynamic. Core, because the dynamic expression tree parsing is still done by using this project. In addition, the project address is in RulesEngine

https://github.com/hueifeng/BlogSample/tree/master/src/RulesEngineDemo

Above is. NET RulesEngine (rule engine) use details, more about. NET RulesEngine (rule engine) use information please pay attention to other related articles on this site!


Related articles: