在操作中添加控制函数以使用 Microsoft Rules Composer 优化规则执行(预览版)
适用于:Azure 逻辑应用(标准)
重要
此功能为预览版,受 Azure 预览版补充使用条款约束。
本指南介绍如何通过使用 Microsoft Rules Composer 向规则中的操作添加控制函数,来优化规则执行。 控制函数可帮助应用程序或规则集控制规则引擎工作内存中的事实。 这些函数包括可用于 .NET 对象和 TypedXmlDocument 实体的 Assert、Clear、Haltt、Retract、RetractByType 、Reassert 和 Update 函数。 工作内存中存在事实会驱动引擎评估的条件以及执行的操作。
先决条件
下载并安装 Microsoft Rules Composer。
包含要处理的规则集的 XML 文件。
若要添加事实,请在从“规则集资源管理器”窗口指向的 XML 文档中指定其值。 或者,可以使用事实创建器向规则引擎提供包含作为事实的 .NET 对象的数组。 有关详细信息,请参阅《生成事实创建器和检索器》。
Assert 函数
若要将对象实例添加到规则引擎的工作内存中,请使用 Microsoft Rules Composer 中的 Assert 函数。 引擎根据针对对象实例类型编写的条件和操作,使用“匹配-冲突解决-操作”阶段来处理每个实例。
下表汇总了支持的断言实体和实例类型的 Assert 函数行为,包括为每个断言实体在引擎中创建的结果实例数,以及应用于每个实例的标识类型。
实体 | 断言的实例数 | 实例类型 |
---|---|---|
.NET 对象 | 1(对象本身) | 完全限定的 .NET 类 |
TypedXmlDocument | 1-N 个 TypedXmlDocument:基于所创建的选择器绑定和文档内容 | DocumentType.Selector |
断言 .NET 对象
规则引擎本机支持引用类型的基本 .NET 标量类型和对象。 断言的 .NET 对象流程是最简单的处理类型。
在 Microsoft Rules Composer 中,可以从规则中断言 .NET 对象。
在“Microsoft Rules Composer”中,加载包含要处理的规则存储的 XML 文件。
在“规则集资源管理器”窗口中,找到并选择所需的规则。
在“THEN”窗格中的“操作”中,添加 Assert 内置函数作为操作。
在“事实资源管理器”窗口中,选择“.NET 类”。
从“.NET 类”选项卡中,将你想要的对象的构造函数方法拖动到“Assert”操作中的参数。
Microsoft Rules Composer 随后将构造函数方法转换为规则定义中“CreateObject”调用。
注意
尽管规则引擎具有“CreateObject”函数,但该函数不会在 Microsoft Rules Composer 中显示为单独的函数。
每个对象作为单独的对象实例断言为工作内存,这意味着引用对象类型的每个谓词(例如 IF Object.Property = 1
)会分析实例。 该实例还可用于引用类型的规则操作(基于规则条件的结果)。
例如,假设你具有以下规则:
Rule 1
IF A.Value = 1
THEN A.Status = "good"
规则 2
IF B.Value = 1
THEN A.Status = "good"
在规则 1 中,只有值为 1 的 A 实例才会更新其“Status”属性。 但是,在规则 2 中,如果条件的计算结果为“true”,则将更新所有 A 实例的状态。 事实上,如果有多个 B 实例,则每当 B 实例条件的计算结果为“true”时,都将更新 A 实例。
断言 TypedXmlDocument 实体
在 Microsoft Rules Composer 中,可以从规则内断言 TypedXmlDocument 实体。
在“规则集资源管理器”窗口中,找到并选择所需的规则。
在“THEN”窗格中的“操作”中,添加 Assert 内置函数作为操作。
在“事实资源管理器”窗口中,选择“XML 架构”。
在“XML 架构”选项卡中,将所需的节点拖动到“Assert”操作中的参数。
XML 文档基本上是文本,但字段值可能属于任何类型,这种类型基于规则生成时指定的类型。 字段是 XPath 表达式,因此它们可能会返回节点集,这意味着节点集中的第一项用作值。
将 TypedXmlDocument 实体断言为事实时,规则引擎会基于规则中定义的选择器创建 TypedXmlDocument 子实例。 你可以将选择器视为隔离 XML 文档中节点的方法,将字段视为标识选择器中的特定项的方法。 规则引擎将一个选择器中的所有字段分组为对象。
选择器也是 XPath 表达式。 在“事实资源管理器”中,选择“XML 架构”选项卡下选择节点时,Microsoft Rules Composer 将自动填充所有节点的“XPath 选择器”属性,以及不包含子节点的所有节点的“XPath 字段”属性。 另外,如果需要,你也可以为“XPath 选择器”和“XPath 字段”输入自己的 XPath 表达式。 如果选择器与 XML 文档中的多个部分相匹配,则将向规则引擎工作内存中断言或从中取消多个此类型的对象。
你可以在同一文档中使用多个选择器。 这样,你就可以查看文档的不同部分(例如,假如一个部分为订单,而另一个部分包含发货地址)。 但请记住,创建的对象由创建它们的 XPath 字符串定义。 如果使用其他 XPath 表达式,则即使表达式解析为同一节点,结果也是唯一的 TypedXmlDocument 实体。
例如,假设有以下 XML:
<root>
<order customer="Joe">
<item name="router" quantity="10" cost="550" />
<item name="switch" quantity="3" cost="300" />
</order>
<order customer="Jane">
<item name="switch" quantity="1" cost="300" />
<item name="cable" quantity="23" cost="9.99" />
</order>
</root>
如果使用选择器 /root/order 或 //order,则以下对象将添加到引擎的工作内存中:
Object 1
<order customer="Joe">
<item name="router" quantity="10" cost="550" />
<item name="switch" quantity="3" cost="300" />
</order>
Object 2
<order customer="Jane">
<item name="switch" quantity="1" cost="300" />
<item name="cable" quantity="23" cost="9.99" />
</order>
在每个选择器中,XPaths 引用各个字段。 因此,如果使用选择器 /root/order/item、//order/item 或 //item,则以下对象将添加到引擎的工作内存中,其中包含 Joe 的两个项目和 Jane 的两个项目:
<root>
<order customer="Joe">
</order>
<order customer="Jane">
</order>
</root>
每个对象都可以访问三个字段:@name、@quantity 和 @cost。 你可以引用父字段,因为对象是对原始文档的引用(例如,../@customer)。
在后台,规则引擎可以通过 XmlConvert 函数将文本字段值转换为支持的任何一种类型。 可以通过在 Microsoft Rules Composer 中设置类型来指定此选项。 如果转换不可行,引擎将引发异常。 只能将 bool 和 double 类型作为其各自的类型(字符串或对象)来检索。
Clear 函数
若要重置规则引擎实例的工作内存和日程,请使用 Microsoft Rules Composer 中的 Clear 函数。 有关工作内存和议程的详细信息,请参阅《规则引擎优化》。
重置规则引擎的工作内存和议程
在“规则集资源管理器”窗口中,找到并选择你要在其中清除规则引擎的工作内存和议程的规则。
在“THEN”窗格中的“操作”下,添加 Clear 内置函数作为操作。
Clear 函数不采用任何参数。
Halt 函数
若要停止规则引擎的当前执行,请使用 Microsoft Rules Composer 中的 Halt 函数。
停止规则集执行
在“规则集资源管理器”窗口中,找到并选择你要在其中停止规则集执行的规则。
在“THEN”窗格中的“操作”下,添加 Halt 内置函数作为操作。
Halt 函数采用单个“布尔”参数。 如果将值指定为“true”,则规则引擎将清除包含挂起的候选规则的议程。
Ruleset.Execute 方法是 RuleEngine.Execute 方法的包装器,它使用类似下方的代码:
RuleEngine.Assert(facts);
RuleEngine.Execute();
RuleEngine.Retract(facts);
如果使用 Ruleset.Execute 方法执行规则集,则当 Halt 函数执行时,规则引擎会将控制权返回到 Ruleset.Execute 方法。 Ruleset.Execute 方法会取消事实并将控制权返回到调用方。 在这种情况下,停止的规则集执行无法恢复。
但是,如果你直接使用 RuleEngine.Execute 方法执行该规则集,则通过再次调用 RuleEngine.Execute 即可用下一个挂起的规则触发恢复已停止的规则集执行(只要没有取消两个调用之间所需的任何对象)。
注意
Ruleset.Execute 方法缓存规则引擎实例以提高性能。 如果直接使用 RuleEngine.Execute 方法,则不会缓存规则引擎实例。
以下示例代码演示如何恢复已停止的规则集执行:
// Assert facts into working memory of the rules engine instance.
RuleEngine.Assert(facts);
// Execute the ruleset.
RuleEngine.Execute();
// The ruleset invokes the Halt method when executing actions.
// Control returns here when the Halt function is called.
// When engine halts, do the following tasks.
// Add your code here.
// Resume the halted rules engine execution.
RuleEngine.Execute();
// Retract or remove facts from working memory in the rules engine.
RuleEngine.Retract(facts);
Retract 函数
若要从规则集和规则引擎的工作内存中删除对象,请使用 Microsoft Rules Composer 中的 Retract 函数。
取消 .NET 对象
在“规则集资源管理器”窗口中,找到并选择所需的规则。
在“THEN”窗格中的“操作”下,添加 Retract 内置函数作为操作。
在“事实资源管理器”窗口中,选择“.NET 类”。
从“.NET 类”选项卡中,将所需的类(而不是程序集或方法)拖动到 Retract 参数 (Parameter) 的引数 (Argument) 中。
如果将方法拖至 Retract 函数中,则引擎将尝试取消该方法所返回的对象。
取消 .NET 对象会有以下影响:
从议程中删除使用这些对象的操作。
注意
在你使用 Retract 函数之前,议程中更高级别的其他操作可能已经执行。
在谓词中使用该对象的规则将会从议程中删除其操作(如果在议程中存在任何操作)。
引擎不再计算对象。
取消一个或多个 TypedXmlDocument 实体
可以取消在规则引擎中断言的原始 TypedXmlDocument 实体,也可以取消从父 XmlDocument 实体创建的 TypedXmlDocument 子实体之一。
假设你有以下示例 XML:
<order>
<orderline customer="Joe" linenumber="001">
<product name="router" quantity="10" cost="550" />
</orderline>
<orderline customer="Jane" linenumber="002">
<product name="switch" quantity="1" cost="300" />
</orderline>
</order>
可以取消与订单对象关联的 TypedXmlDocument 实体,也可以取消与 orderline 对象关联的一个或全部两个 TypedXmlDocument 实体。 所有 TypedXmlDocument 实体都与最初断言的顶级 TypedXmlDocument 实体相关联,而不是与 XML 树层次结构中高于该顶级 TypedXmlDocument 节点的 TypedXmlDocument 实体相关联。
例如,product 是 orderline 对象下面的 TypedXmlDocument 实体,与 order 的 TypedXmlDocument 实体相关联,而不是 orderline 的 TypedXmlDocument 实体。 在大多情况下,此差别并不重要。 但是,如果取消 order 对象,则也将取消 orderline 和 product 对象。 如果取消 orderline 对象,则仅取消该对象,而不会取消 product 对象。
引擎仅处理和跟踪最初断言 TypedXmlDocument 实体时创建的对象实例(即 TypedXmlDocument 实例)。 如果创建其他节点(例如,通过规则集中的选择器选择的节点的同级节点),则除非 TypedXmlDocument 实体是根据这些节点创建和添加的,否则,不会使用规则对这些节点进行评估。 如果断言这些较低级别的新 TypedXmlDocument 实例,则引擎会评估规则中的实例,但顶级 TypedXmlDocument 实体不知道这些实例。 在取消顶级 TypedXmlDocument 时,不会自动取消单独断言的新 TypedXmlDocument 实体。 因此,如果创建新节点,请在完整的 XmlDocument 上执行 Retract 和 Reassert,这是通常使用的最直接的步骤。
TypedXmlDocument 类提供了有用的方法,可以在自定义 .NET 成员中作为操作的一部分调用。 这些方法包括获取与 TypedXmlDocument 或父 TypedXmlDocument 关联的 XmlNode 的功能。
取消顶级 TypedXmlDocument 实体
在“规则集资源管理器”窗口中,找到并选择所需的规则。
在“THEN”窗格中的“操作”下,添加 Retract 内置函数作为操作。
在“事实资源管理器”窗口中,选择“XML 架构”。
从“XML 架构”选项卡中,将架构的顶级节点拖动到 Retract 函数的参数中。
此顶部节点以 .xsd 扩展结尾,表示文档根节点,而不是文档元素节点。 此节点具有一个引用初始 TypedXmlDocument 的 / 选择器。 取消父 TypedXmlDocument 时,将从工作内存中删除与 TypedXmlDocument 关联的所有 TypedXmlDocument 子实体,包括通过调用 Assert 函数创建的所有 TypedXmlDocument 实体,具体取决于规则集中使用的选择器。
取消子 TypedXmlDocument 实体
在“规则集资源管理器”窗口中,找到并选择所需的规则。
在“THEN”窗格中的“操作”下,添加 Retract 内置函数作为操作。
在“事实资源管理器”窗口中,选择“XML 架构”。
从“XML 架构 ”选项卡中,将子节点拖动到 Retract 函数的参数 中。
RetractByType 函数
若要从规则引擎的工作内存中删除指定类型的所有对象,请使用 Microsoft Rules Composer 中的 RetractByType 函数。 此函数不同于 Retract 函数,后者仅删除特定类型的特定项。
取消特定类型的所有 .NET 对象
在“规则集资源管理器”窗口中,找到并选择所需的规则。
在“THEN”窗格中的“操作”下,添加 RetractByType 内置函数作为操作。
在“事实资源管理器”窗口中,选择“.NET 类”。
从“.NET 类”选项卡中,将类拖动到 RetractByType 函数的参数中。
取消特定类型的所有 TypedXmlDocument 实体
RetractByType 删除具有相同 DocumentType.Selector 的所有 TypedXmlDocument 实体。
在“规则集资源管理器”窗口中,找到并选择所需的规则。
在“THEN”窗格中的“操作”下,添加 RetractByType 内置函数作为操作。
在“事实资源管理器”窗口中,选择“XML 架构”。
从“XML 架构”选项卡中,将相应的节点拖动到 RetractByType 函数。
与 Retract 函数一致,如果在文档根节点上使用 RetractByType 函数,此操作不仅会取消与该 DocumentType 一起断言的所有 TypedXmlDocument 实体,而且还会取消与这些父 TypedXmlDocument 实体关联的树层次结构中的所有子 TypedXmlDocument 实体或 XmlNode 节点。
Reassert 函数
若要对引擎的工作内存中已存在的对象调用 Assert 函数,请使用 Microsoft Rules Composer 中的 Reassert 函数。 此行为相当于向该对象发出 Retract 命令,然后执行 Assert 命令。
例如,如果在 .NET 对象上使用 Reassert 函数,规则引擎将执行以下步骤:
从工作内存中取消 .NET 对象。
找到在谓词或操作中使用对象的规则的议程,删除该议程上的任何操作。
将 .NET 对象断言回到工作内存中,并评估为新断言的对象。
重新评估使用谓词中对象的任何规则,并根据需要将这些规则的操作添加到议程中。
为以前评估为 true 的任何规则,将操作重新添加到议程,并且仅使用这些规则的操作中的对象。
重新断言 .NET 对象
在“规则集资源管理器”窗口中,找到并选择所需的规则。
在“THEN”窗格中的“操作”中,添加 Reassert 内置函数作为操作。
在“事实资源管理器”窗口中,选择“.NET 类”。
从“.NET 类”选项卡中,将类拖动到 Reassert 函数的参数中。
重新断言 TypedXmlDocument 实体
在“规则集资源管理器”窗口中,找到并选择所需的规则。
在“THEN”窗格中的“操作”中,添加 Reassert 内置函数作为操作。
在“事实资源管理器”窗口中,选择“XML 架构”。
在“XML 架构”选项卡中,将所需的实体节点拖动到“Reassert”函数中的参数。
如果重新断言顶级 TypedXmlDocument 实体,则第一次断言顶级 TypedXmlDocument 实体时创建的 TypedXmlDocument 子实体的行为可能有所不同,具体取决于每个 TypedXmlDocument 子实体的状态。
例如,如果新的或现有的子实体是“脏的”(dirty),这意味着系统使用操作在规则集中至少更改了一个字段,随后对该子实体执行 Assert 函数或 Reassert 函数。 任何不“脏”的现有子实体都保留在工作内存中。
注意
节点不会从引擎不知道的外部操作中标记为“脏的”,例如,外部应用程序以编程方式添加、删除或更新该节点。
以下示例演示了一个简化的情况,用于描述父实体重新建立时子实体的行为。 假设在工作内存中具有以下 TypedXmlDocument 实体:Parent、Child1、Child2 和 Child3。
- 父实体是顶级 TypedXmlDocument 实体。
- 每个子级都包含一个名为 ExampleField 的字段,其中的值设置为 1,例如
Child1.ExampleField
= 1`。
假设规则操作对子实体执行以下操作:
- Child2 的 ExampleField 值从 1 更新为 0。
- 用户代码删除 Child3。
- 用户代码将名为 NewChild 的新 TypedXmlDocument 子实体添加到 Parent。
以下示例显示了工作内存中对象的新表示形式:
Parent
Child1 // Where Child1.ExampleField = 1
Child2 // Where Child2.ExampleField = 0
NewChild
现在,假设你重新断言 Parent 实体,这会导致以下子实体行为:
- Child2 被重新断言,因为它在更新其字段后现在是“脏的”。
- Child3 从工作内存中取消。
- NewChild 经过断言进入工作内存。
- Child1 在工作内存中保持不变,因为它在 Parent 被重新断言之前未更新。
Update 函数
若要根据新的数据和状态将对象重新断言到用于重新评估的规则引擎中,请使用 Microsoft Rules Composer 中的 Update 函数。 对象可以具有 .NET 类类型或 TypedXmlDocument 类型。 你也可以使用 Update 函数来提高引擎性能并防止发生无穷循环的情况。
重要
重新评估规则的默认最大循环计数为 2^32,因此对于某些规则,规则集执行可能会持续很长时间。 若要减少循环计数,请更改规则集版本上的“最大执行循环深度”属性。
更新 .NET 对象
在“规则集资源管理器”窗口中,找到并选择所需的规则。
在“THEN”窗格中的“操作”下,添加 Update 内置函数作为操作。
在“事实资源管理器”窗口中,选择“.NET 类”。
从“.NET 类”选项卡中,将类拖动到 Update 函数的参数中。
通常,使用 Assert 将新对象放置在规则引擎的工作内存,并使用 Update 来更新工作内存中已经存在的对象。 将新对象断言为事实时,引擎会重新评估所有规则中的条件。 但是,当你更新现有对象时,如果这些条件的计算结果为 true,则引擎仅重新评估使用更新的事实的条件,并将操作添加到议程。
例如,假设你有以下规则,并且名为 ItemA 和 ItemB 的对象已存在于工作内存中。
规则 1 将评估 ItemA 的 Id 属性,并设置 ItemB 的 Id 属性,然后在更改之后重新断言 ItemB。 重新评估 ItemB 时,引擎会将 ItemB 视为新对象,并且引擎将重新评估在谓词或操作中使用 ItemB 的所有规则。 此行为可确保引擎根据规则 1 中的 ItemB.Id 中设置的新值重新评估 规则 2。
规则 1
IF ItemA.Id == 1 THEN ItemB.Id = 2 Assert(ItemB)
规则 2 可能会使第一个评估失败,但在第二次评估期间评估结果为 true。
规则 2
IF ItemB.Id == 2 THEN ItemB.Value = 100
借助将对象重新添加到工作内存的功能,你可以显式控制正向链接情况中的行为。 但是,此示例揭示出同时重新评估规则 1 的副作用。 ItemA.Id 并未发生更改,因此规则 1 评估结果仍为 true ,并将再次触发 Assert(ItemB) 操作。 而这会导致规则产生无穷循环。
防止无限循环
你必须能够在无需创建无限循环的情况下,重新断言对象。 若要避免此类情况,可以使用 Update 函数。 与 Reassert 函数一样,Update 函数对规则操作更改的关联对象实例执行 Retract 和 Assert 函数,但存在以下主要差异:
在议程上,规则的操作仅在操作中使用实例类型而不是谓词时保留在议程上。
仅在操作中使用实例类型的规则不会被重新评估。
因此,仅在谓词中或同时在谓词和操作中使用实例类型的规则会被重新评估,并且这些规则的操作会被相应地添加到议程中。
通过将前面的示例更改为使用 Update 函数,可以确保引擎仅重新评估规则 2,因为规则 2 的条件使用 ItemB。 引擎不会重新评估 规则 1,因为 ItemB 仅在规则 1* 的操作中使用,这能消除循环情况。
Rule 1
IF ItemA.Id == 1
THEN ItemB.Id = 2
Update(ItemB)
规则 2
IF ItemB.Id == 2
THEN ItemB.Value = 100
即便以这种方式使用 Update 函数,仍可能创建循环情况。 例如,考虑以下规则:
IF ItemA.Id == 1
THEN ItemA.Value = 20
Update(ItemA)
谓词使用 ItemA,因此引擎在 ItemA 上调用 Update 时会重新评估规则。 如果 ItemA.Id 的值在其他位置未更改,规则 1 将继续评估为 true,这会导致在 ItemA 上再次调用 Update。
作为规则设计者,你必须确保避免创建此类循环情况。 修复此问题的适当方法因规则的性质而异。
以下示例展示了一种简单解决前面示例中问题的方法,即通过添加对 ItemA.Value 的检查,防止规则在系统第一次执行规则的操作后再次被评估为 true。
IF ItemA.Id == 1 and ItemA.Value != 20
THEN ItemA.Value = 20
Update(ItemA)
更新 TypedXmlDocument 实体
在“规则集资源管理器”窗口中,找到并选择所需的规则。
在“THEN”窗格中的“操作”下,添加 Update 内置函数作为操作。
在“事实资源管理器”窗口中,选择“XML 架构”。
在“XML 架构”选项卡中,将所需的实体节点拖动到 Update 函数中的参数。
例如,假设你具有以下规则:
规则 1 评估采购订单消息中项目的总数。
IF 1 == 1 THEN ProcessPO.Order:/Order/Items/TotalCount = (ProcessPO.Order:/Order/Items/TotalCount + ProcessPO:/Order/Items/Item/Count)
如果总数大于或等于 10,则规则 2 会将状态设置为“需要审批”。
规则 2
IF ProcessPO.Order:/Order/Items/TotalCount >= 10 THEN ProcessPO.Order:/Order/Status = "Needs approval"
如果将以下采购订单消息作为输入传递给此规则集,则你会注意到状态未设置为“需要审批”,即使 TotalCount 为 14。 发生此行为的原因是仅在 TotalCount 值为 0 时,在开始时评估规则 2。 每次更新 TotalCount 时,不会评估该规则。
<ns0:Order xmlns:ns0="http://ProcessPO.Order">
<Items>
<Item>
<Id>ITM1</Id>
<Count>2</Count>
</Item>
<Item>
<Id>ITM2</Id>
<Count>5</Count>
</Item>
<Item>
<Id>ITM3</Id>
<Count>7</Count>
</Item>
<TotalCount>0</TotalCount>
</Items>
<Status>No approval needed</Status>
</ns0:Order>
若要让引擎每次更新 TotalCount 时重新评估条件,必须在更新的节点 (TotalCount) 的父节点 (Items) 上调用 Update 函数。 如果按如下所示更改规则 1,并再测试一次规则,“状态”字段将设置为“需要审批”:
规则 1(已更新)
IF 1 == 1
THEN ProcessPO.Order:/Order/Items/TotalCount = (ProcessPO.Order:/Order/Items/TotalCount + ProcessPO:/Order/Items/Item/Count) AND
Update(ProcessPO.Order:/Order/Items)