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

之前在这篇文章说过,可以使用脚本引擎来将我们需要经常变化的代码进行动态编译执行,自由度非常大,不过对应的需要资源也多。如果只是针对非常具体业务逻辑的变化,可以尝试使用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. 风炫安全WEB安全学习第十九节课 XSS的漏洞基础知识和原理讲解

    风炫安全WEB安全学习第十九节课 XSS的漏洞基础知识和原理讲解 跨站脚本攻击(Cross-site scripting,通常简称为XSS) 反射型XSS原理与演示 交互的数据不会存储在数据库里,一次 ...

  2. 第1章 无所不在的JavaScript

    traceur转码(编译)器 Babel转码(编译)器 JavaScript API 的核心组成部分:ECMASCcript, DOM, BOM 桌面应用:electron 移动应用:Apache C ...

  3. 安卓mbn文件丢失,无法搜索移动信号,工程模式mbn乱改,不用QPST烧录怎样恢复?超简单!

    没有root,工程模式乱改mbn配置选项,导致mbn配置丢失,无法搜索移动网络. 重启若干次改配置都无效,清空网络设置无效,恢复出厂无效,recovery三清无效, 不太想拆机root麻烦,QPST配 ...

  4. sql server 用触发器记录增删改操作(转载)

    数据库结构: CREATE TABLE [dbo].[cg_tz_log] ( [logid] int NOT NULL IDENTITY(1,1) , operate varchar(10), -- ...

  5. 美业黑科技 ▏肌肤管家SkinRun V3S智能肌肤测试仪,实现“护肤”私人定制

    肌肤如同身体,也需要定时的"健康检查",但仅凭肉眼难以窥见深层的肌肤问题.而现在,肌肤管家SkinRun前沿黑科技护肤测试仪--SkinRun V3S便能帮助用户对症下药.肌肤管家 ...

  6. C中的dll 、lib和exe文件

    参考:链接1   链接2 DLL 动态链接库(Dynamic Link Library,缩写为DLL),运行时加载是一个可以被其它应用程序共享的程序模块,其中封装了一些可以被共享的例程和资源.动态链接 ...

  7. java进阶(31)--TreeSet集合、TreeMap集合、自平衡二叉树

    一.TreeSet集合简单 1.TreeSet集合底层是一个TreeMap 2.TreeMap集合底层是一个二叉树 3.放到TreeSet集合的元素等同于放到TreeMap集合的Key部分 4.Tre ...

  8. Mybatis的CRUD 增删改查

    目录 namespace 命名空间 select insert update delete Mybatis 官网: https://mybatis.org/mybatis-3/zh/getting-s ...

  9. ORA-32004解决办法

    ORA-32004 解释是: 产生问题的原因是一个过时的参数,这个参数是早在8i,9i中使用的,在11g中已经废掉了 这个参数就是pfile中的*.sql_trace 用spfile 生成一个pfil ...

  10. Linux下Too many open files问题排查与解决

    作者: Grey 原文地址: Github 语雀 博客园 Too many open files是Linux系统中常见的错误,从字面意思上看就是说程序打开的文件数过多,不过这里的files不单是文件的 ...