这次继续学习WF中规则引擎中的相关知识。WF中的每一个规则都包括下面三部分:
1. 一个条件返回true或false
2. 一个then操作(当条件为true时执行)
3. 一个else操作(当条件为false时执行)
规则对应System.Workflow.Activities.Rules命名空间中中的Rule类,Rule类的ThenActions,ElseActions都是RuleAction对象的集合。RuleAction是一个抽象类,他的派生类RuleStatementAction指定要更新的属性或字段或要使用 CodeDom 类型运行的方法。还有RuleHaltActin和RuleUpdateAction派生类。RuleStatementAction可以使用简单的fieldA=10,或复杂的计算fieldA=fieldC*3.14,RuleStatementAction的语句块将被序列化为CodeDom对象,CodeDom对象来标识内部的源代码的架构,能动态的生成和编译。如果你使用规则编辑器,你可以不用了解他的细节,但是你要自己动态的在代码中声明规则,你就要好好了解下了。RuleHaltAction类导致 RuleSet停止执行并使控制重新返回到调用方法。RuleUpdateAction 类此类用于向规则引擎指示执行此规则的操作集可导致指定的字段或属性(或一个对象的所有字段和属性)发生更改,从而可以重新计算依赖于此字段或属性的任何规则。Update 调用不会阻止操作块中其他操作的执行。修改的字段/属性集(通过 Update 指定或通过对调用的方法使用 RuleWriteAttribute来指定)用于确定此特定规则的操作集完成执行时的链接。
我们可以将规则视为 IF-THEN-ELSE 语句,其中,条件对应于IF,而操作定义 THEN 和 ELSE 子句的行为。这样的一组规则便组成了规则集RuleSet,规则集既支持规则的简单依序执行,也支持规则的复杂正向链接。 规则集可以由PolicyActivity 活动执行。使用PolicyActivity活动时我们只需要将该活动拖到工作流设计器中, 然后配置 PolicyActivity 上的 RuleSetReference 属性,按名称指向 RuleSetCollection 中的 RuleSet。 PolicyActivity 活动创建 RuleSet 类的实例;规则是由 RuleSet 类执行的。下图为VS2008中的规则集编辑器:
我们可以看到每个规则都有一个(priority)优先级的属性,优先级大的会先执行。虽然设置优先级可以改变规则集中规则的执行顺序,但一般都不会去设置该属性,因为WF的规则引擎有重新计算的能力:(forward chaining)正向链接。下面我就来说说规则引擎中非常重要的正向链接特性。
正向链接
RuleSet类的ChainingBehavior属性表示获取或设置 RuleSet中 Rule 类的正向链接行为。决定规则引擎是如何执行正向链接的,这个属性的值是枚举类型RuleChainingBehavior。下面是各个取值的说明:
None(顺序的):指示不执行链接。每个规则只执行一次。
UpdateOnly(仅显示更新):指示如果已执行的操作使用 RuleUpdateAction 明确指定链接,则执行该链接。
Full(完全链接):指示由操作修改字段或属性、为由操作调用的方法指定 RuleWriteAttribute或执行 RuleUpdateAction时执行该链接。
下面举个例子:
Rule | Conditon | ThenAction | ElseAction |
Rule1 | A>10 | B=60 | B=40 |
Rule2 | B>50 | C="preferred" | C="normal" |
Rule3 | D<100 | B=B*0.80 |
我们可以看出A和D是输入变量,B和C是规则中设置的。如果我们使用完全正向链接的选项,规则引擎会自动根据这些规则来觉得是否需要重新计算之前的规则,比如A是12,D是99的话,
1. Rule1 A>10 所以B=60
2. 到了Rule2 B=60,大于50,所以C="preferred"
3. Rule3 D=99 <100 所以B=60*0.80=48
4. 这个时候B=48,B的值改变了,所以Rule2会重新计算,B<50 C="normal"
5. Rule3不会重新计算,因为他包含的变量自从第一次计算后没有变化。
最后的结果是B=48,C="normal",A D没有变化。如果你将正向链接的属性设为None属性的,每个规则将执行一次,最后的结果将是B=48,C="preferred"。
使用Update语句
有的时候我们不需要完全的正向链接,我们使用UpdateOnly,UpdateOnly 选项关闭隐式的、基于属性的链接,并规定链接只应对显式 Update 语句发生。 这使您能够完全控制哪些规则引起重新计算。 这个时候我们必须使用Update语句,通过这个语句可以告诉WF,某个属性一定会被修改了,相关的规则可能需要重新应用。
下面举例说明,
Rule | Conditon | ThenAction | ElseAction |
Rule1 | A>10 | B=60 ,Update(B) | B=40 ,Update(B) |
Rule2 | B>50 | C="preferred",Update(C) | C="normal",Update(C) |
Rule3 | D<100 | B=B*0.80,Update(B) |
Update语句在序列化到.rules文件的时候会生成RuleUpdateAction,每个Update语句表示是否被其他条件更改,使用此选项可以避免导致规则过度(甚至是失控)重复执行的循环依赖性,或者通过消除为提供 RuleSet 的功能完整性所不需要的规则重新计算来提高性能。
禁止规则重算
另一个影响规则计算的是Rule类的ReevaluationBehavior属性,获取或设置一个值,该值指示是否可以重新计算 Rule。其值为RuleReevaluationBehavior,默认是Always,还有Never.对应于上面图中的重新计算选项。
Always :为默认值,它提供了前面讨论过的行为,即,总是根据其他规则的操作所引起的链接重新计算规则。
Never: 顾名思义就是关闭重新计算。 规则计算一次,但如果该规则先前已执行了任何操作,则不进行重新计算。 换言之,如果先前计算过该规则,并因此执行了其 Then 或 Else 操作,则不会重新计算该规则。 但是,执行 Then 或 Else 操作中的空操作集合并不表示规则已经执行。
基于属性
当一个规则的条件或执行为字段或属性的时候,WF的规则引擎可以识别他们的关系来控制正向链接,如果是方法的时候就不行了,比如GetAccountStatus方法,我们可以猜到这个方法是获取Account的状态值,但是WF确猜不到,我们使用下面的一组属性来解决这个问题。
一共三个RuleReadAttribute,RuleWriteAttribute 和RuleInvokeAttribute 类分别表示用于读取条件、写入操作和调用方法的属性。通过属性来标明哪些是对条件表达式的修改。下面是例子:
Rule | Conditon | ThenAction |
Rule1 | this.discount > 0 | this.total = (1-this.discount) * this.subtotal |
Rule2 | this.subtotal > 10000 | this.SetDiscount(0.05) |
然后,可以将 SetDiscount 方法按如下方式进行属性设置。 这会使引擎能够标识规则 1 依赖于规则 2,因为使用了折扣字段。
[RuleWrite("discount")] void SetDiscount(double requestedDiscount) { ...//Some code that updates the discount field. }
RuleReadAttribute和RuleWriteAttribute 用法基本相同,RuleInvokeAttribute 属性可以用于指示由链接的方
法调用导致的依赖项。 例如,假定对规则和方法进行下列修改:
Rule | Conditon | ThenAction |
Rule1 | this.discount > 0 | this.total = (1-this.discount) * this.subtotal |
Rule2 | this.subtotal > 10000 | this.SetDiscountWrapper(0.05) |
[RuleInvoke("SetDiscount")]
void SetDiscountWrapper(double requestedDiscount)
{
SetDiscount(requestedDiscount);
}
[RuleWrite("discount")]
void SetDiscount(double requestedDiscount)
{
}
以下是有关使用属性的一些补充说明:
-
可以使用属性指定如何在方法中使用参数。 例如,通过对以下方法进行属性 (attribute) 设置,指示此方法修改传递的 Order 实例上的 Discount 属性 (property)。
[RuleWrite("currentOrder/Discount", RuleAttributeTarget.Parameter)] private void SetDiscount(Order currentOrder, double discount) { currentOrder.Discount = discount; }
-
在规则属性中还可以使用通配符。 例如,可以使用 RuleWrite("order/*") 指示此方法修改“order”字段引用的对象上的所有字段。 但是,只能在路径的末尾使用通配符;像 RuleWrite("*/Discount") 之类的属性无效。
-
像 RuleWrite("order") 之类的属性可以与引用类型一起使用以指示引用已更改,例如,指示变量目前指向其他 Order 实例。 除了测试实例引用本身的所有规则外,使用字段/属性的所有规则也都假定为受到影响;例如 IF this.order == this.order2。
-
在未指定方法属性情况下的默认行为,是假定方法调用在目标对象(即对其调用方法的对象)上不读取或写入任何字段/属性。 此外,假定该方法调用根据 .NET Framework 中定义的关键字(ref、out、ByVal、ByRef 等)读取参数和写入参数。