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

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

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

简介

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

  • 输入输出类型数量比较固定,但是执行逻辑经常变化;
  • switch条件经常变化,复杂switch语句的替代;
  • 会变动的,具有多种条件或者规则的业务逻辑;
  • 规则自由度不要求特别高的场景。(这种情况建议使用脚本引擎)

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

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

install-pacakge RulesEngine

规则定义

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

[
{
"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逻辑。

{
"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"
}
]
}

示例

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

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

public 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类型,对于程序生成的数据,使用匿名类型更加方便。

var 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的命名空间。如果需要使用到稍微复杂一些的类型,可以自己定义类型或者函数。比如定义一个这样的函数:

public 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:

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

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

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

规则参数

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

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

本地变量

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

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

{
"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也比较简洁,上手非常简单。

C#脚本引擎RulesEngine的更多相关文章

  1. Java 8 的 Nashorn 脚本引擎教程

    本文为了解所有关于 Nashorn JavaScript 引擎易于理解的代码例子. Nashorn JavaScript 引擎是Java SE 8的一部分,它与其它像Google V8 (它是Goog ...

  2. 【开源】.Net 动态脚本引擎NScript

    开源地址: https://git.oschina.net/chejiangyi/NScript 开源QQ群: .net 开源基础服务  238543768 .Net 动态脚本引擎 NScript   ...

  3. [No00007A]没有文件扩展".js"的脚本引擎 解决办法

    在命令行运行JScript脚本时,遇到如下的错误提示: “输入错误: 没有文件扩展“.js”的脚本引擎.” 这样的错误,原因是因为JS扩展名的文件被其他软件关联了,需要取消关联. 如系统中安装了ULT ...

  4. 使用Lua脚本语言开发出高扩展性的系统,AgileEAS.NET SOA中间件Lua脚本引擎介绍

    一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...

  5. cocos2dx的build_win32.dat出现问题以及install-template-msvc.dat出现.js没有脚本引擎

    关于cocos2dx-2.x.x版本当中出现build_win32.bat执行失败 (针对VS2013)应当在VS的安装路径查找msbuild的文件夹,再其中查找msbuild.exe文件找到四个东西 ...

  6. c# 动态执行脚本,相关的几个脚本引擎.

    Jint   嵌入式的javascript脚本支持引擎,一直都在更新,对各种方法支持也比较好,可以 C# 交互. https://github.com/sebastienros/jint Jurass ...

  7. Nmap源码分析(脚本引擎)

    Nmap提供了强大的脚本引擎(NSE),以支持通过Lua编程来扩展Nmap的功能.目前脚本库已经包含300多个常用的Lua脚本,辅助完成Nmap的主机发现.端口扫描.服务侦测.操作系统侦测四个基本功能 ...

  8. 利用Roslyn构建一个简单的C#交互脚本引擎

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 微软的下一代编译器技术Roslyn是一个里程碑的技术,可以给.NET平台带来无限想象空间.比 ...

  9. C#脚本引擎 CS-Script 之(三)——如何部署

    本文不但介绍了CS-Script如何部署,还介绍了CS-Script的部署后面的原理,并用一个框图详细介绍了部署中的各种细节. 一.获取资源 1.从官网上下载编译好的csscript资源:cs-scr ...

随机推荐

  1. 在.NET Core中使用Channel(一)

    我最近一直在熟悉.net Core中引入的新Channel<T>类型.我想在它第一次发布的时候我了解过它,但是有关文章非常非常少,我不能理解它们与其他队列有什么不同. 在使用了一段时间后, ...

  2. Windows server 安装远程桌面及破解120天时间限制授权

    一.问题描述 Windows Server系列服务器默认远程桌面连接数是2个用户(本文适用于所有Windows Server系列服务器),如果多余两个用户进行远程桌面连接时,系统就会提示超过连接数,可 ...

  3. Docker学习笔记之进入容器Bash

    我们在创建容器的时候,如果容器的命令(command)不是/bin/bash的时候,使用docker attach命令是会卡住进不去容器的bash shell的.如下图所示: 所以,这里记录一个可以进 ...

  4. 经常使用的Sublime Text 快捷键

    最常用的 Sublime快捷键:

  5. buuctf刷题之旅—web—WarmUp

    启动靶机 查看源码发现source.php 代码审计,发现hint.php文件 查看hint.php文件(http://7ab330c8-616e-4fc3-9caa-99d9dd66e191.nod ...

  6. 【九阳神功】Nessus 8_VM不限IP及AWVS破解版合体部署

    Nessus 8下载地址: https://moehu-my.sharepoint.com/personal/ximcx_moebi_org/_layouts/15/download.aspx?Sou ...

  7. Hadoop 专栏 - MapReduce 入门

    MapReduce的基本思想 先举一个简单的例子: 打个比方我们有三个人斗地主, 要数数牌够不够, 一种最简单的方法可以找一个人数数是不是有54张(传统单机计算); 还可以三个人各分一摞牌数各自的(M ...

  8. 01-CentOS 8.1安装 Docker

    官方参考地址:https://docs.docker.com/install/linux/docker-ce/centos/ 里面包含包下载地址:https://download.docker.com ...

  9. ReactRouter的实现

    ReactRouter的实现 ReactRouter是React的核心组件,主要是作为React的路由管理器,保持UI与URL同步,其拥有简单的API与强大的功能例如代码缓冲加载.动态路由匹配.以及建 ...

  10. CentOS系统内核升级(在线 离线)

    为什么要升级内核? Docker 在CentOS系统中需要安装在 CentOS 7 64 位的平台,并且内核版本不低于 3.10:CentOS 7.× 满足要求的最低内核版本要求,但由于 CentOS ...