.NET的RulesEngine(规则引擎)使用

1、背景说明

1.1 规则引擎的使用场景

RulesEngine是Microsoft推出的一个规则引擎项目,用于系统中抽象出的业务逻辑/规则/策略。在医疗行业中经常涉及的功能就是知识库或CDSS,这个基本上就是各种各样的规则集合及提示。例如:两个药品之间会有配伍禁忌、相互作用,因此不能一起配液或同时使用;某药品的给药频率为一天一次(QD),不可为一天三次(TID)、一天四次(QID)等。而这种场合的特点就是输入信息基本不变,规则经常变(常常是增加)。现阶段增加意味着增加代码,发布版本。因此普通企业/软件的这种需求,特别适合规则引擎。
偶然在网上看到规则引擎,当天就研究了下,真的是太好用了!所以总结一下

1.2 demo的代码说明

我用的环境:win10企业版、VS2019社区版,.NET Framework 4.7.2,然后新建控制台程序

2、演示

首先从NuGet上下载并安装RulesEngine。如图所示
在这里插入图片描述

2.1 入门demo演示

2.1.1 代码展示

控制台代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using RulesEngine.Models;


namespace REDemo2
{
    class Program
    {
        static async Task Main(string[] args)
        {
            //模拟用户的输入内容
            var userInput = new UserInput
            {
                IdNo = null,
                Age = 18
            };


            //定义规则
            var rulesStr = @"[{
                    ""WorkflowName"": ""UserInputWorkflow"",
                    ""Rules"": [
                      {
                        ""RuleName"": ""CheckAge"",
                        ""ErrorMessage"": ""年龄必须大于18岁."",
                        ""ErrorType"": ""Error"",
                        ""RuleExpressionType"": ""LambdaExpression"",
                        ""Expression"": ""Age > 18""
                      },
                       {
                        ""RuleName"": ""CheckIDNoIsEmpty"",
                        ""ErrorMessage"": ""身份证号不可以为空."",
                         ""ErrorType"": ""Error"",
                        ""RuleExpressionType"": ""LambdaExpression"",
                        ""Expression"": ""IdNo != null""
                      }
                    ]
                  }] ";

            //反序列化Json格式规则字符串
            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("验证成功:{0},消息:{1}", item.IsSuccess, item.ExceptionMessage);
            }

            Console.ReadLine();
        }
    }

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

}

代码的规则,只是简单的验证年龄必须大于18岁及身份证号不能为空。
运行以上代码,展示的结果如下:
在这里插入图片描述
可以通过以上的入门demo展示规则引擎的使用。

2.1.2 代码下载

样例代码下载。代码下载 提取码:NHZL

2.2 规则参数说明

先拿一个完整的样例说一下规则,如下图所示
在这里插入图片描述
整个规则分为如上图的三部分,这三者又是嵌套的关系。其中1和3是必须的,2可有可无。若没有2,则全部按默认处理。这三部分的定义如下

2.2.1 第一部分参数说明

第一部分的参数只有两个。两个都是必须项,具体定义如下

参数名称类型说明必须
WorkflowNamestring定义被规则引擎识别的名称
Rulesarray具体的规则。规则可以进行多层嵌套

2.2.2 第二部分参数说明

第二部分官方文档中称之为Rule。具体定义如下

参数名称类型说明必须
RuleNamestring规则的名称
Operatorenum操作符.用于第3部分多个规则之间的关系。共有4种:AndAndAlsoOrOrElse
ErrorMessagestring错误信息。用于返回的信息
ErrorTypeenum错误类型。共有两种:WarningError
SuccessEventstring完成事件,默认为规则名称
RulesRules规则数组,也就说可以嵌套

2.2.3 第三部分参数说明

第三部分参数在文档中称之为Leaf Rule,意思就是最小的规则。也就是具体规则内容。

参数名称类型说明必须
RuleNamestring规则的名称
Expressionstring表单式,具体的规则内容
RuleExpressionTypeenum虽然是枚举类型,但目前只有一种,只能填写LambdaExpression
ErrorMessagestring提示的错误信息
ErrorTypeenum错误类型。共有两种:WarningError
SuccessEventstring完成事件,默认为规则名称

以上定义的官方文档:官方文档链接

2.3 在Expression使用自定义判断

上面的例子在第三部分的参数(leaf rule)的Expression中,只能使用系统(system)自带的方法进行逻辑判断。即string自带的方法、int自带的方法。若想使用自定义方法,则需要进行额外的处理。

2.3.1 先编写业务逻辑判断方法

首先先编写业务逻辑判断方法,如下所示

    public static class IdCardUtil
    {
        //此处使用了C#的方法扩展
        public static int GetAgeByIdCard(this string str)
        {
            //假设进行了相关处理,得到结果
            return 45;
        }

        //此处是正常的业务逻辑方法
        public static int getAgeByIdCardNo(string str)
        {
            return 50;
        }
    }

2.3.2 通过ReSettings进行增加自定义类型

然后通过ReSettings增加自定义类型

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

2.3.2 修改具体规则

//定义规则
            var rulesStr = @"[{
                    ""WorkflowName"": ""UserInputWorkflow"",
                    ""Rules"": [
                      {
                        ""RuleName"": ""CheckNestedSimpleProp"",
                        ""ErrorMessage"": ""年龄必须小于18岁."",
                        ""ErrorType"": ""Error"",
                        ""RuleExpressionType"": ""LambdaExpression"",
                        ""Expression"": ""IdCardUtil.getAgeByIdCardNo(IdNo) < 18""   //这是常用的方法。
                        ""Expression"": ""IdNo.GetAgeByIdCard() < 18""    // 这个是使用C#自定义扩展方法。 这两句任选一个就可以。
                      },
                       {
                        ""RuleName"": ""CheckNestedSimpleProp1"",
                        ""ErrorMessage"": ""身份证号不可以为空."",
                         ""ErrorType"": ""Error"",
                        ""RuleExpressionType"": ""LambdaExpression"",
                        ""Expression"": ""IdNo != null""
                      }
                    ]
                  }] ";

规则中""Expression"": ""IdCardUtil.getAgeByIdCardNo(IdNo) < 18""""Expression"": ""IdNo.GetAgeByIdCard() < 18""的效果是一样的,任选一个就OK了

2.3.3 将自定义类型进行注册

将自定义的内容进行注册

var rulesEngine = new RulesEngine.RulesEngine(workflowRules.ToArray(),null,reSettings); //添加reSettings内容

然后就可以正常运行了。

2.3.4 代码下载

源码下载。代码 提取码:NHZL

2.4 其他参数说明

2.4.1 RuleParameter(规则参数)

可以对输入内容可以使用RuleParameter来进行封装具体的参数类型。

//这是原始的输入
var zhenglininfo = new UserInput
{
    IdNo = null,
    Age = 18
};
//使用RuleParameter进行封装			
RulesEngine.Models.RuleParameter ruleParameter = new RulesEngine.Models.RuleParameter("Test", zhenglininfo);
.....
//相应的修改
List<RulesEngine.Models.RuleResultTree> resultList = await rulesEngine.ExecuteAllRulesAsync("UserInputWorkflow", ruleParameter);

2.4.2 LocalParams(本地变量)

这个比较重要,这个会将多个条件进行拆分,并分别单独命名,然后使用这些命名进行逻辑判断。例如

            var rulesStr = @"[{
                    ""WorkflowName"": ""UserInputWorkflow"",
                    ""Rules"": [
                                    {
                                        ""RuleName"": ""CheckAge"",
                                        ""ErrorMessage"": ""年龄必须大于18岁."",
                                        ""ErrorType"": ""Error"",
                                        ""localParams"": [
                                          {
                                                ""Name"": ""model1"",
                                                ""Expression"": ""Age!=0""
                                          },
                                          {
                                                ""Name"": ""model2"",
                                                ""Expression"": ""IdCardUtil.getAgeByIdCardNo(Test.IdNo) < 18""
                                          }
                                        ],
                                        ""RuleExpressionType"": ""LambdaExpression"",
                                        ""Expression"": ""model1 AND model2""
                                    },
                                    {
                                        ""RuleName"": ""CheckIDNoIsEmpty"",
                                        ""ErrorMessage"": ""身份证号不可以为空."",
                                         ""ErrorType"": ""Error"",
                                        ""RuleExpressionType"": ""LambdaExpression"",
                                        ""Expression"": ""IdNo != null ""
                                    }
                                ]
                            }] ";

上面就是本地变量的规则样例,其中model2中,传入了Test.IdNo,这就是用到了2.4.1的RuleParameter。

2.4.2.1 代码下载

LocalParams的代码下载。下载地址 提取码:NHZL

2.4.3 GlobalParams(全局变量)

GlobalParams(全局变量)是定义在workflow层面,并且可以在任何rule中使用。例如:

//Rule.json
{
  "WorkflowName": "workflowWithGlobalParam",
  "GlobalParams":[
    {
      "Name":"myglobal1",
      "Expression":"myInput.hello.ToLower()"
    }
  ],
  "Rules":[
    {
      "RuleName": "checkGlobalEqualsHello",
      "Expression":"myglobal1 == \"hello\""
    },
    {
      "RuleName": "checkGlobalEqualsInputHello",
      "Expression":"myInput.hello.ToLower() == myglobal1"
    }
  ]
}

全局变量会在所有的rule中使用,下面的语句将返回true

  var input = new RuleParameter("myInput",new {
    hello = "HELLO"
  });

  var resultList  = await re.ExecuteAllRulesAsync("workflowWithGlobalParam",rp);

2.4.3 返回结果

执行完成后,需要查看返回结果。有两种方式查看:

2.4.3.1 方法1
//使用规则进行判断,并返回结果
var resultList = await rulesEngine.ExecuteAllRulesAsync("UserInputWorkflow", ruleParameter);

foreach (var item in resultList)
{
    Console.WriteLine("验证成功:{0},消息:{1}", item.IsSuccess, item.ExceptionMessage);
}
2.4.3.2 方法2
var resultList = rulesEngine.ExecuteAllRulesAsync("UserInputWorkflow", ruleParameter).Result;

            resultList.OnSuccess((eventName) => {
                Console.WriteLine("{0}是ok!", eventName);
            }).OnFail(() => {
                Console.WriteLine("失败了!");
            });

两个方法略有不同

2.5 后续学习

规则引擎主要是System.Linq.Dynamic.Core,下一步继续学习下这块内容

2.6 参考资料

主要参考资料有:
1、官网资料。官网
2、波多尔斯基 的《C#规则引擎RulesEngine》
3、微软MVP精选 | .NET RulesEngine(规则引擎)

  • 7
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值