贪婪指数 發表於 2022-10-27 08:39:00

C# RulesEngine 规则引擎:从入门到看懵

<p></p><div class="toc"><div class="toc-container-header">目录</div><ul><li>说明</li><li>安装</li><li>怎么使用</li><li>多参数</li><li>全局参数、本地参数<ul><li>全局参数</li><li>本地参数</li></ul></li><li>定义验证成功、失败行为</li><li>计算折扣</li><li>使用自定义函数<ul><li>静态函数</li><li>实例函数</li></ul></li><li>自定义执行器</li></ul></div><p></p>
<h3 id="说明">说明</h3>
<p>RulesEngine 是 C# 写的一个规则引擎类库,读者可以从这些地方了解它:</p>
<p>仓库地址:</p>
<p>https://github.com/microsoft/RulesEngine</p>
<p>使用方法:</p>
<p>https://microsoft.github.io/RulesEngine</p>
<p>文档地址:</p>
<p>https://github.com/microsoft/RulesEngine/wiki</p>
<p><strong>什么是规则引擎?</strong></p>
<blockquote>
<p>照搬 https://github.com/microsoft/RulesEngine/wiki/Introduction#what-is-the-rules-engine</p>
</blockquote>
<p>在企业项目中,关键或核心部分总是业务逻辑或业务规则,也就是 CRUD,这些系统都有一个共同的特征是,某个模块中的一些或许多规则或策略总会发生变化,例如购物网站的顾客折扣、物流企业的运价计算等。随着这些变化而来的是大量的重复工作,如果系统没有足够的抽象,那么每当增加一种规则时,开发者需要在规则、回归测试、性能测试等方面的变化中编写代码。</p>
<p>在 RulesEngine 中,微软对规则进行了抽象,这样核心逻辑总是得到稳定的、易于维护的,而规则的更改可以以一种简单的方式生成,而不需要更改代码库。此外,系统的输入本质上是动态的,因此不需要在系统中定义模型,而是可以作为扩展对象或任何其他类型的对象作为输入,系统经过预定义的规则处理后,输出结果。</p>
<p>它有以下特性:</p>
<ul>
<li>Json based rules definition    (基于 Json 的规则定义)</li>
<li>Multiple input support    (多输入支持)</li>
<li>Dynamic object input support    (动态对象输入支持)</li>
<li>C# Expression support    (C # 表达式支持)</li>
<li>Extending expression via custom class/type injection    (通过自定义类/类型注入扩展表达式)</li>
<li>Scoped parameters    (范围参数)</li>
<li>Post rule execution actions    (发布规则执行操作)</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/1315495/202210/1315495-20221026222200640-1421850882.png" alt="" loading="lazy"></p>
<p>说人话就是,业务逻辑的输出结果受到多个因子影响,但是这些影响有一定规律的,那么适合将这些部分抽象出来,接着使用规则引擎处理,例如购物的各种优惠卷叠加之后的最终折扣价、跨区运输的不同类型的包裹运价计算等。</p>
<p>笔者认为这个规则引擎主要由两部分构成:</p>
<ul>
<li>规则验证系统,例如根据规则验证字段、执行函数验证当前流程、输出执行结果;</li>
<li>动态代码引擎,能够将字符串转换为动态代码,利用表达式树这些完成;</li>
</ul>
<p>当然,这样说起来其实很抽象的,还得多撸代码,才能明白这个 RulesEngine 到底是干嘛的。</p>
<h3 id="安装">安装</h3>
<p>新建项目后,nuget 直接搜索 <code>RulesEngine</code> 即可安装,在 nuget 介绍中可以看到 <code>RulesEngine</code> 的依赖:</p>
<p><img src="https://img2022.cnblogs.com/blog/1315495/202210/1315495-20221026222224138-773198575.png" alt="" loading="lazy"></p>
<p>FluentValidation 是一个用于构建强类型验证规则的 .NET 库,在 ASP.NET Core 项目中,我们会经常使用模型验证,例如必填字段使用 <code></code>、字符串长度使用 <code></code> 等;但是因为是特性注解,也就是难以做到很多需要经过动态检查的验证方式,使用 FluentValidation 可以为模型类构建更加丰富的验证规则。</p>
<p>而 FluentValidation 用在 RulesEngine 上,也是相同的用途,RulesEngine 最常常用做规则验证,检查模型类或业务逻辑的验证结果,利用 FluentValidation中丰富的验证规则,可以制作各种方便的表达式树,构建动态代码。</p>
<h3 id="怎么使用">怎么使用</h3>
<p>我们通过 RulesEngine 检查模型类的字段是否符合规则,来了解 RulesEngine 的使用方法。</p>
<p>创建一个这样的模型类:</p>
<pre><code class="language-csharp">public class Buyer
{
    public int Id { get; set; }
    public int Age { get; set; }
    // 是否为已认证用户
    public bool Authenticated { get; set; }
}
</code></pre>
<p>场景是这样的,用户下单购买商品,后台需要判断此用户<strong>是否已经成年</strong>、<strong>是否通过了认证</strong>。</p>
<p>正常来看代码应该这样写:</p>
<pre><code>if(Authenticated == true &amp;&amp; Age &gt; 18)
</code></pre>
<p>但是如果年龄调为 16 岁呢?如果最近公司搞活动,不需要上传身份证就能购买商品呢?</p>
<p>当然定义变量存储到数据库也行,但是如果后面又新增了几个条件,那么我们就需要修改代码了,大佬说,这样不好,我们要 RulesEngine 。</p>
<p>好的,那我们来研究一下这个东西。</p>
<p>前面提到的 <code>if(Authenticated == true &amp;&amp; Age &gt; 18)</code>,这么一个完整的验证过程,在 RulesEngine 称为 Workflow,每个 Workflow 下有多个 Rule。</p>
<pre><code>if(Authenticated == true &amp;&amp; Age &gt; 18) =&gt; Workflow
          Authenticated == true                          =&gt; Rule
          Age &gt; 18                                                  =&gt; Rule
</code></pre>
<p>在 RulesEngine中,有两种方法定义这些 Workflow 和 Rule,一种是使用代码,一种是 JSON,官方是推荐使用 JSON 的,因为 JSON 可以动态生成,可以实现真正的动态。</p>
<p>下面我们来看看如何使用 JSON 和代码,分别定义<code>if(Authenticated == true &amp;&amp; Age &gt; 18)</code> 这个验证过程。</p>
<p>JSON 定义:</p>
<pre><code class="language-json">[
{
    "WorkflowName": "Test",
    "Rules": [
      {
      "RuleName": "CheckAuthenticated",
      "Expression": "Authenticated == true"
      },
      {
      "RuleName": "CheckAge",
      "Expression": "Age &gt;= 18"
      }
    ]
}
]
</code></pre>
<pre><code class="language-csharp">      var rulesStr = "[{... ...}]" // JSON
      var workflows = JsonConvert.DeserializeObject&lt;List&lt;Workflow&gt;&gt;(rulesStr);
</code></pre>
<p>C# 代码:</p>
<pre><code class="language-csharp">      var workflows = new List&lt;Workflow&gt;();
      List&lt;Rule&gt; rules = new List&lt;Rule&gt;();

      Workflow exampleWorkflow = new Workflow();
      exampleWorkflow.WorkflowName = "Test";
      exampleWorkflow.Rules = rules;
      workflows.Add(exampleWorkflow);

      Rule authRule = new Rule();
      authRule.RuleName = "CheckAuthenticated";
      authRule.Expression = "Authenticated == true";
      rules.Add(authRule);

      Rule ageRule = new Rule();
      ageRule.RuleName = "CheckAge";
      ageRule.Expression = "Age &gt;= 18";
      rules.Add(ageRule);
</code></pre>
<p>两种方式都是一样的,每个 Workflow 下有多个 Rule,可以定义多个 Workflow。</p>
<p>当前我们有两个地方要了解:</p>
<pre><code class="language-json">      "RuleName": "CheckAuthenticated",
      "Expression": "Authenticated == true"
</code></pre>
<p><code>RuleName</code>:规则名称;</p>
<p><code>Expression</code>: 真实的代码,必须是符合 C# 语法的代码;</p>
<p>定义好 Workflow 和 Rule 后,我们需要生成规则引擎,直接 <code>new RulesEngine.RulesEngine()</code> 即可:</p>
<pre><code class="language-csharp">      var bre = new RulesEngine.RulesEngine(workflows.ToArray());
</code></pre>
<blockquote>
<p>生成引擎是需要一些时间的。</p>
</blockquote>
<p>生成引擎后,我们通过名称指定调用一个 Workflow,并获取每个 Rule 的验证结果:</p>
<pre><code class="language-csharp">      List&lt;RuleResultTree&gt; resultList = await bre.ExecuteAllRulesAsync("Test", new Buyer
      {
            Id = 666,
            Age = 17,
            Authenticated = false
      });
</code></pre>
<p>完整代码示例如下:</p>
<pre><code class="language-csharp">    static async Task Main()
    {
      // 定义
      var rulesStr = ... ...// JSON
      // 生成 Workflow[ Rule[] ]
      var workflows = JsonConvert.DeserializeObject&lt;List&lt;Workflow&gt;&gt;(rulesStr)!;
      var bre = new RulesEngine.RulesEngine(workflows.ToArray());

      // 调用指定的 Workflow,并传递参数,获取每个 Rule 的处理结果
      List&lt;RuleResultTree&gt; resultList = await bre.ExecuteAllRulesAsync("Test", new Buyer
      {
            Id = 666,
            Age = 17,
            Authenticated = false
      });

      // 打印输出
      foreach (var item in resultList)
      {
            Console.WriteLine("规则名称:{0},    验证结果:{1}", item.Rule.RuleName, item.IsSuccess);
      }
    }
</code></pre>
<p><img src="https://img2022.cnblogs.com/blog/1315495/202210/1315495-20221026222242403-625229554.png" alt="" loading="lazy"></p>
<h3 id="多参数">多参数</h3>
<p>如果商品需要 VIP 才能购买呢?</p>
<p>这里我们再定义一个模型类,表示一个用户是否为 VIP。</p>
<pre><code class="language-csharp">public class VIP
{
    public int Id { get; set; }
    public bool IsVIP { get; set; }
}
</code></pre>
<p>那么这个时候就需要处理两个模型类了,为了能够在 Rule 中使用所有的模型类,我们需要为每个模型类定义 <code>RuleParameter</code>。</p>
<pre><code class="language-csharp">      var rp1 = new RuleParameter("buyer", new Buyer
      {
            Id = 666,
            Age = 20,
            Authenticated = true
      });

      var rp2 = new RuleParameter("vip", new VIP
      {
            Id = 666,
            IsVIP = false
      });
</code></pre>
<blockquote>
<p>相当于表达式树:</p>
<pre><code>            ParameterExpression rp1 = Expression.Parameter(typeof(Buyer), "buyer");
            ParameterExpression rp2 = Expression.Parameter(typeof(VIP), "vip");
</code></pre>
<p>可以参考笔者的表达式树系列文章:https://ex.whuanle.cn/</p>
</blockquote>
<p>然后重新设计 JSON,增加一个 Rule:</p>
<pre><code class="language-json">[{
        "WorkflowName": "Test",
        "Rules": [{
                        "RuleName": "CheckAuthenticated",
                        "Expression": "buyer.Authenticated == true"
                },
                {
                        "RuleName": "CheckAge",
                        "Expression": "buyer.Age &gt;= 18"
                },
                {
                        "RuleName": "CheckVIP",
                        "Expression": "vip.IsVIP == true"
                }
        ]
}]
</code></pre>
<p>然后执行此 Workflow:</p>
<pre><code class="language-csharp">List&lt;RuleResultTree&gt; resultList = await bre.ExecuteAllRulesAsync("Test", rp1, rp2);
</code></pre>
<p><img src="https://img2022.cnblogs.com/blog/1315495/202210/1315495-20221026222302309-871052552.png" alt="" loading="lazy"></p>
<p>完整代码:</p>
<pre><code class="language-csharp">    static async Task Main()
    {
      // 定义
      var rulesStr = ... ... // JSON
      var workflows = JsonConvert.DeserializeObject&lt;List&lt;Workflow&gt;&gt;(rulesStr)!;
      var bre = new RulesEngine.RulesEngine(workflows.ToArray());

      var rp1 = new RuleParameter("buyer", new Buyer
      {
            Id = 666,
            Age = 20,
            Authenticated = true
      });

      var rp2 = new RuleParameter("vip", new VIP
      {
            Id = 666,
            IsVIP = false
      });

      List&lt;RuleResultTree&gt; resultList = await bre.ExecuteAllRulesAsync("Test", rp1, rp2);

      foreach (var item in resultList)
      {
            Console.WriteLine("规则名称:{0},    验证结果:{1}", item.Rule.RuleName, item.IsSuccess);
      }
    }
</code></pre>
<h3 id="全局参数本地参数">全局参数、本地参数</h3>
<h4 id="全局参数">全局参数</h4>
<p>在 Workflow 中可以定义全局参数,参数对 Workflow 内的所有 Rule 起效,所有 Rule 都可以使用它。</p>
<p>定义示例:</p>
<pre><code class="language-json">        "WorkflowName": "Test",
        "GlobalParams": [{
                "Name": "age",
                "Expression": "buyer.Age"
        }],
</code></pre>
<p>参数的值,可以定义为常量,也可以来源于传入的参数。</p>
<p>修改上一个小节的示例,在 Rule <code>CheckAge</code> 中,使用这个全局参数。</p>
<p><img src="https://img2022.cnblogs.com/blog/1315495/202210/1315495-20221026222317940-153742129.png" alt="" loading="lazy"></p>
<pre><code class="language-json">[{
        "WorkflowName": "Test",
        "GlobalParams": [{
                "Name": "age",
                "Expression": "buyer.Age"
        }],
        "Rules": [{
                        "RuleName": "CheckAuthenticated",
                        "Expression": "buyer.Authenticated == true"
                },
                {
                        "RuleName": "CheckAge",
                        "Expression": "age &gt;= 18"
                },
                {
                        "RuleName": "CheckVIP",
                        "Expression": "vip.IsVIP == true"
                }
        ]
}]
</code></pre>
<h4 id="本地参数">本地参数</h4>
<p>本地参数在 Rule 内定义,只对当前 Rule 起效。</p>
<p><img src="https://img2022.cnblogs.com/blog/1315495/202210/1315495-20221026222331768-394451361.png" alt="" loading="lazy"></p>
<pre><code class="language-csharp">[{
        "WorkflowName": "Test",
        "Rules": [{
                        "RuleName": "CheckAuthenticated",
                        "LocalParams": [{
                                "Name": "age",
                                "Expression": "buyer.Age"
                        }],
                        "Expression": "buyer.Authenticated == true"
                },
                {
                        "RuleName": "CheckAge",
                        "Expression": "age &gt;= 18"
                },
                {
                        "RuleName": "CheckVIP",
                        "Expression": "vip.IsVIP == true"
                }
        ]
}]
</code></pre>
<p>在定义参数时,参数的值可以通过执行函数来获取:</p>
<pre><code class="language-csharp">      "LocalParams":[
      {
          "Name":"mylocal1",
          "Expression":"myInput.hello.ToLower()"
      }
      ],
</code></pre>
<p><code>LocalParams</code> 可以使用 <code>GlobalParams</code> 的参数再次生成新的变量。</p>
<p><img src="https://img2022.cnblogs.com/blog/1315495/202210/1315495-20221026222407127-847129731.png" alt="" loading="lazy"></p>
<pre><code class="language-csharp">"GlobalParams":[
    {
      "Name":"myglobal1"
      "Expression":"myInput.hello"
    }
],
"Rules":[
    {
      "RuleName": "checkGlobalAndLocalEqualsHello",
      "LocalParams":[
      {
          "Name": "mylocal1",
          "Expression": "myglobal1.ToLower()"
      }
      ]
    },
</code></pre>
<h3 id="定义验证成功失败行为">定义验证成功、失败行为</h3>
<p>可以为每个 Rule 定义验证成功和失败后执行一些代码。</p>
<p>格式示例:</p>
<pre><code class="language-csharp">      "Actions": {
         "OnSuccess": {
            "Name": "OutputExpression",
            "Context": {
               "Expression": "input1.TotalBilled * 0.8"
            }
         },
         "OnFailure": {
               "Name": "EvaluateRule",
               "Context": {
                   "WorkflowName": "inputWorkflow",
                   "ruleName": "GiveDiscount10Percent"
               }
         }
      }
</code></pre>
<p><code>OutputExpression</code> 里面定义了执行代码:</p>
<pre><code class="language-json">            "Name": "OutputExpression",
            "Context": {
               "Expression": "input1.TotalBilled * 0.8"
            }
</code></pre>
<p><code>EvaluateRule</code> 定义了执行另一个 Workflow 的 Rule,</p>
<pre><code class="language-json">               "Name": "EvaluateRule",
               "Context": {
                   "WorkflowName": "inputWorkflow",
                   "ruleName": "GiveDiscount10Percent"
               }
</code></pre>
<p><img src="https://img2022.cnblogs.com/blog/1315495/202210/1315495-20221026222421257-1218574297.png" alt="" loading="lazy"></p>
<p>在<code>OnSuccess</code> 、<code>OnFailure</code> 里面,内部结构如下所示:</p>
<pre><code class="language-json">            "Name": "OutputExpression",//Name of action you want to call
            "Context": {//This is passed to the action as action context
               "Expression": "input1.TotalBilled * 0.8"
            }

            "Name": "EvaluateRule",
               "Context": {
                   "WorkflowName": "inputWorkflow",
                   "ruleName": "GiveDiscount10Percent"
               }
            
</code></pre>
<p><code>Name:{xxx}</code> 中的 <code>{xxx}</code> 是一个具体的执行器名称,不是随便定义的,<code>OutputExpression</code>、<code>EvaluateRule</code> 都是自带的执行器,所谓的执行器就是一个 <code>Func&lt;ActionBase&gt;</code>,在后面的 自定义执行器 中,可以了解更多。</p>
<p><code>Context</code> 里面的内容,是一个字典,这些 <code>Key/Value</code> 会被当做参数传递给执行器,每个执行器要求设置的 Context 是不一样的。</p>
<p>另外每个 Rule 都可以定义以下三个字段:</p>
<pre><code class="language-json">      "SuccessEvent": "10",
      "ErrorMessage": "One or more adjust rules failed.",
      "ErrorType": "Error",
</code></pre>
<p><code>ErrorType</code> 有两个选项,<code>Warn</code>、<code>Error</code>,如果这个 Rule 的表达式错误,那么是否弹出异常。如果设置为 <code>Warn</code>, Rule 有问题,验证结果则会是 false,而不会报异常;如果是 <code>Error</code>,那么这个 Rule 会中止 Workflow 的执行,程序会报错。</p>
<p><img src="https://img2022.cnblogs.com/blog/1315495/202210/1315495-20221026222439812-1816603418.png" alt="" loading="lazy"></p>
<p><code>SuccessEvent</code> 跟 <code>ErrorMessage</code> 对应,只是成功、失败的提示消息。</p>
<h3 id="计算折扣">计算折扣</h3>
<p>前面提到的都是验证规则,接下来我们将会使用 RulesEngine 实现规则计算。</p>
<p>这里规定,基础折扣为 1.0,如果用户小于 18 岁,打 9 折,如果用户是 VIP,打 9 折,两个规则独立。</p>
<pre><code>如果是小于 18岁,则 1.0 * 0.9
如果是 VIP,    则 1.0 * 0.9
</code></pre>
<p>定义一个模型类,用于传递折扣基值。</p>
<pre><code class="language-csharp">// 折扣
public class Discount
{
    public double Value
    {
      get; set;
    }
}
</code></pre>
<p>定义三个参数:</p>
<pre><code class="language-csharp">      var rp1 = new RuleParameter("buyer", new Buyer
      {
            Id = 666,
            Age = 16,
      });

      var rp2 = new RuleParameter("vip", new VIP
      {
            Id = 666,
            IsVIP = true
      });

      var rp3 = new RuleParameter("discount", new Discount
      {
            Value = 1.0
      });
</code></pre>
<p>定义规则计算,每个规则计算的是自己的折扣:</p>
<pre><code class="language-csharp">[{
        "WorkflowName": "Test",
        "GlobalParams": [{
                "Name": "value",
                "Expression": "discount.Value"
        }],
        "Rules": [{
                        "RuleName": "CheckAge",
                        "Expression": "buyer.age &lt; 18",
                        "Actions": {
                                "OnSuccess": {
                                        "Name": "OutputExpression",
                                        "Context": {
                                                "Expression": "value * 0.9"
                                        }
                                }
                        }
                },
                {
                        "RuleName": "CheckVIP",
                        "Expression": "vip.IsVIP == true",
                        "Actions": {
                                "OnSuccess": {
                                        "Name": "OutputExpression",
                                        "Context": {
                                                "Expression": "value * 0.9"
                                        }
                                }
                        }
                }
        ]
}]
</code></pre>
<p><img src="https://img2022.cnblogs.com/blog/1315495/202210/1315495-20221026222456286-224822919.png" alt="" loading="lazy"></p>
<p>完整代码:</p>
<pre><code class="language-csharp">    static async Task Main()
    {
      // 定义
      var rulesStr =... ... // JSON
      var workflows = JsonConvert.DeserializeObject&lt;List&lt;Workflow&gt;&gt;(rulesStr)!;
      var bre = new RulesEngine.RulesEngine(workflows.ToArray());

      var rp1 = new RuleParameter("buyer", new Buyer
      {
            Id = 666,
            Age = 16,
      });

      var rp2 = new RuleParameter("vip", new VIP
      {
            Id = 666,
            IsVIP = true
      });

      var rp3 = new RuleParameter("discount", new Discount
      {
            Value = 1.0
      });

      List&lt;RuleResultTree&gt; resultList = await bre.ExecuteAllRulesAsync("Test", rp1, rp2, rp3);
      var discount = 1.0;
      foreach (var item in resultList)
      {
            if (item.ActionResult != null &amp;&amp; item.ActionResult.Output != null)
            {
                Console.WriteLine($"{item.Rule.RuleName} 折扣优惠:{item.ActionResult.Output}");
                discount = discount * (double)item.ActionResult.Output;
            }
      }
      Console.WriteLine($"最终折扣:{discount}");
    }
</code></pre>
<p>笔者这里的示例是,每个规则只计算自己的折扣,也就是每个 Rule 都是独立的,下一个 Rule 不会在上一个 Rule 结果上计算。</p>
<pre><code>&lt; 18 : 0.9
VIP: 0.9
</code></pre>
<p>如果是折扣可以叠加,那么就是 <code>0.9*0.9</code> ,最终可以拿到 <code>0.81</code> 的折扣。</p>
<p>如果折扣不能叠加,只能选择最佳的优惠,那么就是 <code>0.9</code>。</p>
<h3 id="使用自定义函数">使用自定义函数</h3>
<p>自定义函数有两种静态函数和实例函数两种,我们可以在 <code>Expression</code> 中调用预先写好的函数。</p>
<p>下面讲解如何在 Rule 中调用自定义的函数。</p>
<h4 id="静态函数">静态函数</h4>
<p>自定义静态函数:</p>
<pre><code class="language-csharp">    public static bool CheckAge(int age)
    {
      return age &gt;= 18;
    }
</code></pre>
<p>注册类型:</p>
<pre><code class="language-csharp">      ReSettings reSettings = new ReSettings
      {
            CustomTypes = new[] { typeof(Program) }
      };

      var bre = new RulesEngine.RulesEngine(Workflows: workflows.ToArray(), reSettings: reSettings);
</code></pre>
<p>使用静态函数:</p>
<pre><code class="language-json">[{
        "WorkflowName": "Test",
        "Rules": [{
                "RuleName": "CheckAge",
                "Expression": "Program.CheckAge(buyer.Age) == true"
        }]
}]
</code></pre>
<p>完整代码:</p>
<pre><code class="language-csharp">    static async Task Main()
    {
      // 定义
      var rulesStr = "[{\"WorkflowName\":\"Test\",\"Rules\":[{\"RuleName\":\"CheckAge\",\"Expression\":\"Program.CheckAge(buyer.Age) == true\"}]}]";
      var workflows = JsonConvert.DeserializeObject&lt;List&lt;Workflow&gt;&gt;(rulesStr)!;

      ReSettings reSettings = new ReSettings
      {
            CustomTypes = new[] { typeof(Program) }
      };

      var bre = new RulesEngine.RulesEngine(Workflows: workflows.ToArray(), reSettings: reSettings);
      List&lt;RuleResultTree&gt; resultList = await bre.ExecuteAllRulesAsync("Test", new Buyer
      {
            Age = 16
      });

      foreach (var item in resultList)
      {
            Console.WriteLine("规则名称:{0},    验证结果:{1}", item.Rule.RuleName, item.IsSuccess);
      }
    }

    public static bool CheckAge(int age)
    {
      return age &gt;= 18;
    }
</code></pre>
<h4 id="实例函数">实例函数</h4>
<p>定义实例函数:</p>
<pre><code class="language-csharp">    public bool CheckAge(int age)
    {
      return age &gt;= 18;
    }
</code></pre>
<p>通过 <code>RuleParameter</code> 参数的方式,传递实例:</p>
<pre><code>      var rp1 = new RuleParameter("p", new Program());
</code></pre>
<p>通过参数的名称调用函数:</p>
<pre><code class="language-json">[{
        "WorkflowName": "Test",
        "Rules": [{
                "RuleName": "CheckAge",
                "Expression": "p.CheckAge(buyer.Age) == true"
        }]
}]
</code></pre>
<p>完整代码:</p>
<pre><code class="language-csharp">    static async Task Main()
    {
      // 定义
      var rulesStr = "[{\"WorkflowName\":\"Test\",\"Rules\":[{\"RuleName\":\"CheckAge\",\"Expression\":\"p.CheckAge(buyer.Age) == true\"}]}]";
      var workflows = JsonConvert.DeserializeObject&lt;List&lt;Workflow&gt;&gt;(rulesStr)!;

      var rp1 = new RuleParameter("p", new Program());

      var bre = new RulesEngine.RulesEngine(Workflows: workflows.ToArray());
      List&lt;RuleResultTree&gt; resultList = await bre.ExecuteAllRulesAsync("Test", new Buyer
      {
            Age = 16
      }, rp1);

      foreach (var item in resultList)
      {
            Console.WriteLine("规则名称:{0},    验证结果:{1}", item.Rule.RuleName, item.IsSuccess);
      }
    }

    public bool CheckAge(int age)
    {
      return age &gt;= 18;
    }
</code></pre>
<h3 id="自定义执行器">自定义执行器</h3>
<p>自定义执行器就是 <code>OnSuccess</code>、<code>OnFailure</code> 这部分的自定义执行代码,相比静态函数、实例函数,使用自定义执行器,可以获取 Rule 的一些数据。</p>
<pre><code class="language-csharp">                "Actions": {
                        "OnSuccess": {
                                "Name": "MyCustomAction",
                                "Context": {
                                        "customContextInput": "0.9"
                                }
                        }
                }
</code></pre>
<p>自定义一个执行器,执行器需要继承 <code>ActionBase</code>。</p>
<pre><code class="language-csharp">public class MyCustomAction : ActionBase
{
    public override async ValueTask&lt;object&gt; Run(ActionContext context, RuleParameter[] ruleParameters)
    {
      var customInput = context.GetContext&lt;string&gt;("customContextInput");
      return await ValueTask.FromResult(new object());
    }
}
</code></pre>
<p>定义 ReSettings,并在构建规则引擎时,传递进去:</p>
<pre><code class="language-csharp">      var b = new Buyer
      {
            Age = 16
      };
      var reSettings = new ReSettings
      {
            CustomActions = new Dictionary&lt;string, Func&lt;ActionBase&gt;&gt;
            {
                {"MyCustomAction", () =&gt; new MyCustomAction() }
            }
      };

      var bre = new RulesEngine.RulesEngine(workflows.ToArray(), reSettings);

      List&lt;RuleResultTree&gt; resultList = await bre.ExecuteAllRulesAsync("Test", b);
</code></pre>
<p>定义 JSON 规则:</p>
<pre><code class="language-csharp">[{
        "WorkflowName": "Test",
        "Rules": [{
                "RuleName": "CheckAge",
                "Expression": "Age &lt;= 18 ",
                "Actions": {
                        "OnSuccess": {
                                "Name": "MyCustomAction",
                                "Context": {
                                        "customContextInput": "0.9"
                                }
                        }
                }
        }]
}]
</code></pre>
<p><img src="https://img2022.cnblogs.com/blog/1315495/202210/1315495-20221026222515498-57900382.png" alt="" loading="lazy"></p>


</div>
<div id="MySignature" role="contentinfo">
    痴者工良(https://whuanle.cn)<br><br>
来源:https://www.cnblogs.com/whuanle/p/16830333.html
頁: [1]
查看完整版本: C# RulesEngine 规则引擎:从入门到看懵