C# 规则引擎RulesEngine

当编写应用程序时,经常性需要花费大量的时间与精力处理业务逻辑,往往业务逻辑的变化需要重构或者增加大量代码,对开发测试人员很不友好。

之前在这篇文章说过,可以使用脚本引擎来将我们需要经常变化的代码进行动态编译执行,自由度非常大,不过对应的需要资源也多。如果只是针对非常具体业务逻辑的变化,可以尝试使用RulesEngine对程序进行操作。

下文使用了官方示例且部分内容翻译自说明文档

简介

RulesEngine是微软推出的规则引擎,规则引擎在很多企业开发中有所应用,是处理经常变动需求的一种优雅的方法。个人任务,规则引擎适用于以下的一些场景:

  • 输入输出类型数量比较固定,但是执行逻辑经常变化;

  • switch条件经常变化,复杂switch语句的替代;

  • 会变动的,具有多种条件或者规则的业务逻辑;

  • 规则自由度不要求特别高的场景。(这种情况建议使用脚本引擎)

RulesEngine的规则使用JSON进行存储,通过lambda表达式方式表述规则(Rules)。

安装很方便,直接使用nuget进行安装:

Copyinstall-pacakge RulesEngine

规则定义

需要有Rules,有WorkflowName,然后还有一些属性。

Copy[{"WorkflowName": "Discount","Rules": [{"RuleName": "GiveDiscount10","SuccessEvent": "10","ErrorMessage": "One or more adjust rules failed.","ErrorType": "Error","RuleExpressionType": "LambdaExpression","Expression": "input1.country == \"india\" AND input1.loyalityFactor <= 2 AND input1.totalPurchasesToDate >= 5000 AND input2.totalOrders > 2 AND input3.noOfVisitsPerMonth > 2"}]}
]

除了标准的RuleExpressionType,还可以通过定义Rules嵌套多个条件,下面是Or逻辑。

Copy{
"RuleName": "GiveDiscount30NestedOrExample",
"SuccessEvent": "30",
"ErrorMessage": "One or more adjust rules failed.",
"ErrorType": "Error",
"Operator": "OrElse",
"Rules":[{"RuleName": "IsLoyalAndHasGoodSpend","ErrorMessage": "One or more adjust rules failed.","ErrorType": "Error","RuleExpressionType": "LambdaExpression","Expression": "input1.loyalityFactor > 3 AND input1.totalPurchasesToDate >= 50000 AND input1.totalPurchasesToDate <= 100000"},{"RuleName": "OrHasHighNumberOfTotalOrders","ErrorMessage": "One or more adjust rules failed.","ErrorType": "Error","RuleExpressionType": "LambdaExpression","Expression": "input2.totalOrders > 15"}
]
}

示例

可以从官方的代码库中下载示例,定义了上述规则,就可以直接开始用了。示例描述了这么一个应用场景:

根据不同的客户属性,提供不同的折扣。由于销售的情况变化较快,提供折扣的规则也需要经常变动。因此比较适用于规则引擎。

Copypublic void Run()
{Console.WriteLine($"Running {nameof(BasicDemo)}....");//创建输入var basicInfo = "{\"name\": \"hello\",\"email\": \"abcy@xyz.com\",\"creditHistory\": \"good\",\"country\": \"canada\",\"loyalityFactor\": 3,\"totalPurchasesToDate\": 10000}";var orderInfo = "{\"totalOrders\": 5,\"recurringItems\": 2}";var telemetryInfo = "{\"noOfVisitsPerMonth\": 10,\"percentageOfBuyingToVisit\": 15}";var converter = new ExpandoObjectConverter();dynamic input1 = JsonConvert.DeserializeObject<ExpandoObject>(basicInfo, converter);dynamic input2 = JsonConvert.DeserializeObject<ExpandoObject>(orderInfo, converter);dynamic input3 = JsonConvert.DeserializeObject<ExpandoObject>(telemetryInfo, converter);var inputs = new dynamic[]{input1,input2,input3};//加载规则var files = Directory.GetFiles(Directory.GetCurrentDirectory(), "Discount.json", SearchOption.AllDirectories);if (files == null || files.Length == 0)throw new Exception("Rules not found.");var fileData = File.ReadAllText(files[0]);var workflowRules = JsonConvert.DeserializeObject<List<WorkflowRules>>(fileData);//初始化规则引擎var bre = new RulesEngine.RulesEngine(workflowRules.ToArray(), null);string discountOffered = "No discount offered.";//执行规则List<RuleResultTree> resultList = bre.ExecuteAllRulesAsync("Discount", inputs).Result;//处理结果resultList.OnSuccess((eventName) => {discountOffered = $"Discount offered is {eventName} % over MRP.";});resultList.OnFail(() => {discountOffered = "The user is not eligible for any discount.";});Console.WriteLine(discountOffered);
}

输入

输入一般来说是IEnumerable<dynamic>或者是匿名类型,上面实例展示的是由json反序列化形成的dynamic类型,对于程序生成的数据,使用匿名类型更加方便。

Copyvar nestedInput = new {SimpleProp = "simpleProp",NestedProp = new {SimpleProp = "nestedSimpleProp",ListProp = new List<ListItem>{new ListItem{Id = 1,Value = "first"},new ListItem{Id = 2,Value = "second"}}}};

命名空间

和脚本引擎一样,默认规则引擎只能访问System的命名空间。如果需要使用到稍微复杂一些的类型,可以自己定义类型或者函数。比如定义一个这样的函数:

Copypublic static class Utils
{public static bool CheckContains(string check, string valList){if (String.IsNullOrEmpty(check) || String.IsNullOrEmpty(valList))return false;var list = valList.Split(',').ToList();return list.Contains(check);}
}

需要使用的时候,先将类传递给RulesEngine:

Copyvar reSettingsWithCustomTypes = new ReSettings { CustomTypes = new Type[] { typeof(Utils) } };
var engine = new RulesEngine.RulesEngine(workflowRules.ToArray(), null, reSettingsWithCustomTypes);

然后就可以直接在表达式中使用了。

Copy"Expression": "Utils.CheckContains(input1.country, \"india,usa,canada,France\") == true"

规则参数

默认情况下,规则的输入使用的是类似input1 input2这样的形式,如果想直观一点,可以使用RuleParameter来进行封装具体的参数类型。

CopyRuleParameter ruleParameter = new RuleParameter("NIP", nestedInput);
var resultList = bre.ExecuteAllRulesAsync(workflow.WorkflowName, ruleParameter).Result;

本地变量

如果表达式比较复杂的情况下,可以使用本地变量来进行分段处理,这对调试来说会比较方便。

本地变量的关键字为localParams,可以将中间的内容简单理解成var name = expression

Copy{"name": "allow_access_if_all_mandatory_trainings_are_done_or_access_isSecure","errorMessage": "Please complete all your training(s) to get access to this content or access it from a secure domain/location.","errorType": "Error","localParams": [{"name": "completedSecurityTrainings","expression": "MasterSecurityComplainceTrainings.Where(Status.Equals(\"Completed\", StringComparison.InvariantCultureIgnoreCase))"},{"name": "completedProjectTrainings","expression": "MasterProjectComplainceTrainings.Where(Status.Equals(\"Completed\", StringComparison.InvariantCultureIgnoreCase))"},{"name": "isRequestAccessSecured","expression": "UserRequestDetails.Location.Country == \"India\" ? ((UserRequestDetails.Location.City == \"Bangalore\" && UserRequestDetails.Domain=\"xxxx\")? true : false):false"}],"expression": "(completedSecurityTrainings.Any() && completedProjectTrainings.Any()) || isRequestAccessSecured "}

总结

使用规则引擎,可以将经常变动的业务逻辑独立摘出来,为我们编写动态、可拓展的程序提供了很大的便利。RulesEngine这个东西提供的API也比较简洁,上手非常简单。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/305031.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windbg设置条件断点

条件断点&#xff08;condition breakpoint&#xff09;的是指在上面3种基本断点停下来后&#xff0c;执行一些自定义的判断。在基本断点命令后加上自定义调试命令&#xff0c;可以让调试器在断点触发停下来后&#xff0c;执行调试器命令。每个命令之间用分号分割。语法格式如:…

还不知道这 11 个超酷的编程新工具你就 out 了!

工具对开发人员来说至关重要。工具可以让一个开发人员的日常工作更高效&#xff0c;并且只需要关注最重要的事情。对于开发人员来讲&#xff0c;寻找更好的替代工具往往比坚持使用熟悉过时的工具更困难。 在这篇文章中&#xff0c;我们将列出你在日常工作中能够使用的新的开发工…

thinkphp5 异步调用方法_thinkphp5 swoole 执行异步任务

目录结构&#xff1a;服务器端&#xff1a;/**author:hdj*/namespace app\Console;use think\console\Command;use think\console\Input;use think\console\Output;class Websocket extends Command{protected $server;protected function configure(){$this->setName(webso…

python单击url下载网页文件_使用不带url的python脚本从网页下载文件,调用onClick函数 - javascript...

有一个网页带有链接“单击下载”&#xff0c;单击该链接可以下载文件。 我可以通过转到网页并单击此链接来手动下载此文件&#xff0c;但是我需要通过python脚本下载此文件。 如果我看到源代码&#xff0c;则可以看到锚标记将运行js函数 Download in csv 但是我不知道csv文件的…

Linux装多个apache,windows linux如何安装多个apache?

富国沪深Windows下安装多个Apache服务&#xff1a;1.安装好Apache以后&#xff0c;可以在浏览器中输入http://localhost测试&#xff1b;2.更改第一个站点的根目录&#xff1a;在文件Apache2.2\conf\httpd.conf中查找 DocumentRoot 属性&#xff0c;将后面的路径改为你的主站点…

[Abp 源码分析]DTO 自动验证

点击上方蓝字关注我们0.简介在平时开发 API 接口的时候需要对前端传入的参数进行校验之后才能进入业务逻辑进行处理&#xff0c;否则一旦前端传入一些非法/无效数据到 API 当中&#xff0c;轻则导致程序报错&#xff0c;重则导致整个业务流程出现问题。用过传统 ASP.NET MVC 数…

五分钟搞懂并查集

并查集是我暑假从高手那里学到的一招&#xff0c;觉得真是太精妙的设计了。来看一个实例&#xff0c;杭电1232畅通工程。首先在地图上给你若干个城镇&#xff0c;这些城镇都可以看作点&#xff0c;然后告诉你哪些对城镇之间是有道路直接相连的。最后要解决的是整幅图的连通性问…

openwrt安装蒲公英_网速不给力?双宽带叠加,立马消除卡顿

公司有两条宽带&#xff0c;一直想充分利用起来&#xff0c;不过网上搜了下&#xff0c;好像只有软路由或路由器刷了OPWRT之后才能双宽带叠加。软路由需要一定的DIY知识&#xff0c;刷Opwrt需要对编程有一定的了解&#xff0c;这两样我都不会&#xff0c;本想放弃时&#xff0c…

linux强大功能,linux grep和find 的强大功能

grep和find在linux中查找是很强大的&#xff0c;把grep及find的用法及参数列表做个备份以备以后查找&#xff1a;1.grep 命令&#xff0c;grep命令有个常用的参数使用方法如下grep -[acinv]-a 以文本文件方式搜索 -c 计算找到的符合行的次数 -i 忽略大小写 -n 顺便输出行号-R 读…

windows7 + vs2008 + oracle + iis7 客户端配置成功

可以做开发,有兴趣的朋友可以试试. 网上有中文语言包

[Abp 源码分析]多语言(本地化)处理

点击上方蓝字关注我们0.简介如果你所开发的需要走向世界的话&#xff0c;那么肯定需要针对每一个用户进行不同的本地化处理&#xff0c;有可能你的客户在日本&#xff0c;需要使用日语作为显示文本&#xff0c;也有可能你的客户在美国&#xff0c;需要使用英语作为显示文本。如…

Kaggle 发布首份数据科学从业报告 | 不及美国同行1/3,中国数据科学家平均年薪约3万美元

Kaggle 是互联网上最著名的数据科学竞赛平台之一&#xff0c;今年 3 月 8 日&#xff0c;这家机构被谷歌收购&#xff0c;6 月 6 日又宣布用户数量超过了 100 万人。互联网创业方兴未艾&#xff0c;人工智能的浪潮又接踵而来&#xff0c;而贯穿其中的数据科学则在这更迭交替中显…

sequelize模型关联_关于Sequelize连接查询时inlude中model和association的区别详解

前言大家都知道在使用Sequelize进行关系模型(表)间连接查询时&#xff0c;我们会通过model/as来指定已存在关联关系的连接查询模型&#xff0c;或是通过association来直接指定连接查询模型关系。那么&#xff0c;两者各应该在什么场景下使用呢&#xff1f;一、 示例准备模型定义…

mysql设置表名字为占位符_这可能是把MySQL存储引擎讲解的最清楚的一篇文章了

存储引擎是MySQL的组件&#xff0c;用于处理不同表类型的SQL操作。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能&#xff0c;使用不同的存储引擎&#xff0c;还可以获得特定的功能。使用哪一种引擎可以灵活选择&#xff0c;一个数据库中多个表可以使用不同引擎…

linux开发板显示横向彩虹,给 Linux 终端的输出添加彩虹特效的命令

原标题&#xff1a;给 Linux 终端的输出添加彩虹特效的命令正文如果认为Linux命令行很无聊并且没有任何乐趣&#xff0c;那么您错了&#xff0c;真实的Linux多么有趣和淘气。在本文&#xff0c;我将介绍一个名为“ lolcat ”的实用小工具&#xff0c;该实用工具在终端中产生彩虹…

Delphi 与 DirectX 之 DelphiX(46): TDIB.DoAntiAlias;

本例效果图:代码文件:unit Unit1;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, DIB, StdCtrls;typeTForm1 class(TForm)DXPaintBox1: TDXPaintBox;Button1: TButton;Button2: TButton;procedure Button1Click(Sender: T…

Web API实现微信公众平台开发-服务器验证

背景最近开发微信公众号相关接口&#xff0c;在这里记录下微信公众号相关各项功能的实现。先决条件1、一台可部署web服务的服务器或者云平台&#xff08;本地可以搞个花生壳域名&#xff09;。2、一个可以正常使用的微信公众账号&#xff0c;开始的时候使用它的测试号。3、Visu…

每天20分钟,只需一年,一年级学生英语听力达到六年级水平!关键是坚持一点都不难!

导读&#xff1a; 除了语文数学这种常规科目&#xff0c;最让家长们焦虑的就是英语。现在的孩子&#xff0c;英语启蒙都很早&#xff0c;但是对英语的兴趣总是开始还可以&#xff0c;越往后越没动力和兴趣&#xff0c;稍微遇到点挫折就不想坚持了。钱也花了&#xff0c;好老师也…

python爬取论坛付费内容_Python爬虫抓取论坛关键字过程解析

前言&#xff1a; 之前学习了用python爬虫的基本知识&#xff0c;现在计划用爬虫去做一些实际的数据统计功能。由于前段时间演员的诞生带火了几个年轻的实力派演员&#xff0c;想用爬虫程序搜索某论坛中对于某些演员的讨论热度&#xff0c;并按照日期统计每天的讨论量。 这个项…

pygame里面物体闪烁运动_Pygame-游戏中的运动

本来&#xff0c;在上一次pygame的教程中&#xff0c;我只是顺手拿了微信“打飞机”里的图来演示用鼠标控制图片位置的操作。后来觉得&#xff0c;这个游戏还算比较适合用来做例子&#xff0c;也有朋友反馈说想做这个游戏&#xff0c;那不如就以“打飞机”为例来说python游戏开…