现在的位置: 首页 > 综合 > 正文

WF学习系列之三:RuleSet知识点概述

2011年03月02日 ⁄ 综合 ⁄ 共 9741字 ⁄ 字号 评论关闭
 

注:学习RuleSet真的需要耐心,就想我们小学的时候学习加减乘除的时候,首先是了解规则,然后是多加练习,最后才能熟练运用。这个学习的过程是一个反复的过程,就如辩证法所讲的那样:理论-实践-理论。

 

RuleSet 由一个或多个规则条件及其导致的操作组成。可以将规则视为 IF-THEN-ELSE 语句,其中,条件对应于 IF,而操作定义 THEN 和 ELSE 子句的行为。

操作可以执行以下任务:

·    设置工作流上的字段或属性。

·    调用工作流上或工作流中的对象上的方法。

·    调用被引用程序集中的类型上的静态方法。

·    执行 Halt 或 Update 语句。

·    Windows Workflow Foundation 规则引擎设计为以基于优先级的方式运行规则并且支持正向链接。

1规则集中的规则计算

    规则技术在 Windows Workflow Foundation 中以两种主要方式展现:

        作为活动的条件。

        作为 PolicyActivity 活动中的正向链接规则集。

 

    本节稍后的部分将讨论正向链接,但是简而言之,它是指一种能力,这种能力使一个规则的操作可以导致其他相关规则被重新计算

    开发人员使用规则条件而不是代码条件的主要原因是,规则条件已成为模型的一部分,并且可在执行工作流实例时进行运行时动态更新。 规则条件的第二个优点是,在其成为模型的一部分之后,可在该模型的基础之上生成更多复杂的工具,以提供额外的创作体验、依赖关系管理、跨条件分析等等。

    PolicyActivity 活动封装了 RuleSet 的定义和执行。 RuleSet 是具有一组执行语义的规则的集合。 而规则就是对工作流成员进行操作的 If-Then-Else 表达式。

 

    规则计算

    RuleSet 中的每条规则都有默认值为0的优先级值。 可以将 RuleSet 中的规则视为一个有序集合,该集合按优先级值排序。 Windows Workflow Foundation 规则计算器逐条计算规则,并根据规则条件的计算结果执行该规则的操作。

 

计算机制从概念上可描述为:

    1 从活动规则的列表开始。

    2 查找优先级最高的规则。

    3 计算该规则,然后相应地执行其 Then/Else 操作。

    4 如果某条规则的操作更新某个字段或属性,并且该字段或属性由列表中前面的一条或多条规则(这些规则具有更高的优先级)的条件所使用,则重新计算前面的这些规则。

注意: 

只有那些有着特定依赖关系的规则才会被重新计算。

    5 继续执行该过程,直至 RuleSet 中的所有规则都已计算(或者执行了 Halt)。

 

在下面的概念性示例中,假定有下面的规则集,其中AB等等表示工作流中的数据。

 

Rule 4 (Priority = 4)

IF A = 15

THEN B = 5

 

Rule 3 (Priority = 3)

IF C = 5

THEN B = 10

 

Rule 2 (Priority 2)

IF D = 2

THEN A = 15

 

Rule 1 (Priority 1)

IF B = 5

THEN E = 7假定有以下输入数据

 

A =0

 

B = 0

 

C = 5

 

D = 2

 

E = 0

 

计算过程将按如下方式继续

 

计算规则 4;计算结果为 false,因为它没有 Else 操作,所以不执行任何操作。

 

规则 3 的计算结果为 true,因此执行其操作,设置 B = 10。规则 4 不依赖于 B 的值;因此,计算过程继续执行至规则 2。

 

规则 2 的计算结果为 true,因此执行其操作,设置 A = 15。

 

规则 3 和 2 不会重新计算,因为它们的条件不依赖于 A 的值。但是,由于规则 4 在其条件中使用了 A 的值,因此它将被重新计算。 它的计算结果为 true,因此执行其操作,设置 B = 5。规则 4、3 和 2 不依赖于 B 的值,因此,计算过程继续执行至规则 1。

 

规则 1 的计算结果为 true,因此执行其操作,设置 E = 7。

 

现在,结果数据集如下所示:

 

A = 15

 

B = 5

 

C = 5

 

D = 2

 

E = 7

 

2根据优先级执行 RuleSet

    如前所述,如果要以某种顺序执行 RuleSet 或该 RuleSet 中规则的子集,则可以使用规则上的优先级字段精确定义此顺序。 经常这样做可以避免进行链接,甚至在这些方案中可以关闭链接。

    请注意,使用 Windows Workflow Foundation 中的正向执行机制可以定义执行顺序,但并不要求您这样做。大多数情况下,通过正向链接行为就可以获得正确的 RuleSet 结果,不必分配规则优先级,这是因为引擎自动管理各种关系,可以确保规则依赖关系得到满足。

以下示例说明了这一点。

Rule 1

IF this.Weather.Temperature < 50

THEN this.Drink.Style = "Latte"

 

Rule 2

IF this.Drink.Style == "Latte"

THEN this.Snack.Style = "Scone"

ELSE this.Snack.Style = "Muffin"

Windows Workflow Foundation 中可以对规则 1 提供更高的优先级以使其首先执行。 这样可以确保在计算规则 2 之前设置 Drink.Style。

    但是,并非一定需要排序才能获得所需的结果。 假定首先计算规则 2。 在此情况下,Drink.Style 可能为空,也可能为其他样式。 这会导致将 Snack.Style 设置为Muffin。 但是,执行规则 1 并将 Drink.Style 设置为Latte之后,就会重新计算规则 2,并会将 Snack.Style 设置为Scone。 实质上,您可以控制排序,但许多情况下不必这样做。

    如果规则有依赖关系,则排序可能也很有用,但在本应需要显式 Update 语句或方法属性设置的情况却不必这样做。

 

3处理规则中的集合

    在某些情况下,可能必须分别根据集合中的所有项计算规则。可通过多种方法来迭代集合,但其中一种方法是使用规则模式,如下所示:

Rule 1 (Priority = 2) // always execute this rule once to create the enumerator

IF 1==1

THEN this.enumerator = this.myCollection.GetEnumerator()

 

Rule 2 (Priority = 1)

IF this.enumerator.MoveNext()

THEN this.currentInstance = this.enumerator.Current

 

Rules 3-N (Priority = 0)

.... // Additional rules written against this.currentInstance

 

Rule N+1 (Priority = -1)

// can be any condition as long as it is evaluated every time;

// this.currentInstance will be evaluated each time this.currentInstance changes, whereas

// "1==1" would only be evaluated once.

IF this.currentInstance == this.currentInstance 

   

THEN ...

         Update("this/enumerator") //this will cause Rule 2 to be reevaluated

ELSE ...

          Update("this/enumerator")

 

4规则的正向链接

    链接基于规则中标识的依赖项更具体而言基于一个规则的操作和其他规则的条件中的依赖项。 可以通过以下三种方式之一标识或声明这些依赖项:

隐式

基于属性

显式

 

    (1) 隐式

    隐式依赖项由工作流运行时引擎自动标识。 当首次执行 RuleSet 时,将分析每个规则以评估其在自己的条件中读取并在操作中写入的字段/属性。这是通过遍历条件中的表达式和操作中的语句来完成的。 例如,假定有以下规则:

Rule 1

IF this.discount > 0

THEN this.total = (1-this.discount) * this.subtotal

 

Rule 2

IF this.subtotal > 10000

THEN this.discount = 0.05

    工作流运行时引擎将计算这些规则,确定规则 1 读取折扣和小计字段,并写入合计字段。 规则 2 读取小计字段并写入折扣字段;因此,规则 2 上有规则 1 的依赖项。结果是工作流运行时引擎将确保每当规则 2 运行 Then 操作时就会计算或重新计算规则 1。

 

    依赖项在叶节点级别进行标识,如下面的 RuleSet 示例所示。

Rule 1

IF this.order.Discount > 0

THEN this.order.Total = (1-this.order.Discount) * this.order.Subtotal

 

Rule 2

IF this.order.Subtotal > 10000

THEN this.order.Discount= 0.05

 

Rule 3

IF this.order.CustomerType = "Residential"

THEN ...

    仍会标识规则 1 和规则 2 之间的依赖关系。但是,在规则 1 或规则 2 上没有规则 3 的依赖项,因为它们两个都不更新 CustomerType 属性。 也就是说,在 CustomerType 属性的级别上标识依赖项,而不是 Order 对象本身。

    通过隐式链接,Windows Workflow Foundation 为用户完成大部分所需的链接。 因此,用户不必显式建立更新模型,通常可以不关心链接或对它的需要。 这使复杂规则集的建模更加简单并且通常为工作流引擎的隐藏功能。

    其他两种驱动链接的机制(基于属性和显式)是为更复杂和更具体的方案提供的。

 

    (2) 基于属性

    对于在一个规则内调用的方法,更难明确地计算发生的读/写。 为了解决此问题,Windows Workflow Foundation 提供三个属性,可将这些属性应用到某个方法以指示其操作:

RuleRead

RuleWrite

RuleInvoke

    使用 RuleReadAttribute 属性 (attribute) 对方法进行属性设置指示该方法读取所指示的属性 (property)。 同样,可以使用 RuleWriteAttribute 属性 (attribute) 来指示方法将更新给定的字段或属性 (property)。 假定将隐式节下的前两个规则重写为:

 

Rule 1

IF this.discount > 0

THEN this.total = (1-this.discount) * this.subtotal

 

Rule 2

IF this.subtotal > 10000

THEN this.SetDiscount(0.05)

然后,可以将 SetDiscount 方法按如下方式进行属性设置。 这会使引擎能够标识规则 1 依赖于规则 2,因为使用了折扣字段。

 

[RuleWrite("discount")]

void SetDiscount(double requestedDiscount)

{

...//Some code that updates the discount field.

}

 

    RuleInvokeAttribute 属性可以用于指示由链接的方法调用导致的依赖项。 例如假定对规则和方法进行下列修改

Rule 1

IF this.discount > 0

THEN this.total = (1-this.discount) * this.subtotal

 

Rule 2

IF this.subtotal > 10000

THEN this.SetDiscountWrapper(0.05)

 

[RuleInvoke("SetDiscount")]

void SetDiscountWrapper(double requestedDiscount)

{

     ...

     SetDiscount(requestedDiscount);

     ...

}

 

[RuleWrite("discount")]

void SetDiscount(double requestedDiscount)

{

}

规则 2 的操作调用 SetDiscountWrapper 此方法又调用写入折扣字段的 SetDiscount RuleInvokeAttribute 使工作流运行时引擎能够声明和检测到这种间接写入。

应该认识到在属性 (attribute) 路径中引用的字段或属性 (property) 指的是与该方法位于同一类上的字段或属性 (property) 这不一定是传递给 RuleSet 以执行的根对象。 例如,可能会按如下方式对

Order 类进行属性设置:

public class Order

{

     private double discount;

 

     public double Discount

     {

         get { return discount;}

         set { discount = value;}

     }

 

     [RuleWrite("Discount")]

     void CalculateDiscount(double requestedDiscount, double weighting)

     {

...       //Some code that updates the discount field.

     }

}

然后,可以在工作流中使用此类的实例,如下所示:

public class Workflow1 : SequentialWorkflowActivity

{

     private Order discount;

     ...

}

执行规则 2 会导致重新计算规则 1:

 

Rule 1

IF this.order.Discount > 5

THEN ...

 

Rule 2

IF ...

THEN this.order.CalculateDiscount( 5.0, .7)

 

使用属性

以下是有关使用属性的一些补充说明:

1) 可以使用属性指定如何在方法中使用参数。 例如,通过对以下方法进行属性 (attribute) 设置,指示此方法修改传递的 Order 实例上的 Discount 属性 (property)。

[RuleWrite("currentOrder/Discount", RuleAttributeTarget.Parameter)]

private void SetDiscount(Order currentOrder, double discount)

{

    currentOrder.Discount = discount;

}

2) 在规则属性中还可以使用通配符。 例如可以使用 RuleWrite("order/*") 指示此方法修改order字段引用的对象上的所有字段。 但是只能在路径的末尾使用通配符 RuleWrite("*/Discount") 之类的属性无效。

3) RuleWrite("order") 之类的属性可以与引用类型一起使用以指示引用已更改例如指示变量目前指向其他 Order 实例。 除了测试实例引用本身的所有规则外使用字段/属性的所有规则也都假定为受到影响例如 IF this.order == this.order2

4) 在未指定方法属性情况下的默认行为,是假定方法调用在目标对象(即对其调用方法的对象)上不读取或写入任何字段/属性。 此外,假定该方法调用根据 .NET Framework 中定义的关键字(ref、out、ByVal、ByRef 等)读取参数和写入参数。

 

    (3) 显式

    指示字段/属性依赖项的最后一种机制是使用 Update 语句。 Update 语句将表示字段或属性的路径或表示字段/属性访问的表达式作为其参数。例如,可以将以下两个语句之一键入 RuleSet 编辑器中,以创建针对工作流上Customer 实例的 Name 属性的 Update 语句。

Update("this/customer/Name")

OR

Update(this.customer.Name)

注意

RuleSet 编辑器始终将更新显示为 Update("this/customer/Name")

 

    Update 语句指示规则写入指示的字段/属性。 这与在规则中直接设置字段/属性或使用字段/属性的 RuleWriteAttribute 调用方法的效果相同。

    Update 语句也支持通配符的使用。 例如,可以将以下 Update 语句添加到规则中:

    Update("this/customer/*")这会导致对在条件中使用 Customer 实例上的任何属性的所有规则进行重新计算。 也就是说,将重新计算下面的两个规则:

IF this.customer.ZipCode == 98052

THEN ...

 

IF this.customer.CreditScore < 600

THEN ...

    通常在大多数情况下,不必对显式 Update 语句建模;隐式链接会提供所需的依赖项分析和链接。方法属性设置支持大多数隐式链接无法标识依赖项的一般情况。 一般情况下,通过方法属性设置来指示依赖项比使用 Update 语句更可取,因为在方法中标识一次依赖项就可以在许多使用该方法的不同规则中使用该依赖项。 此外,在规则编写者和方法实施者不是同一个人(或者编写和实施工作在不同的时间进行)的情况下,方法属性设置使方法编写者能够更好地理解代码,从而能够标识属于该方法的依赖项。

    但是,在某些情况下,Update 语句是适当的解决方案,例如在将字段/属性传递给您(工作流编写者)不能控制、因而无法进行属性设置的类上的方法时。下面的示例说明了这一点。

IF ...

THEN this.customer.UpdateCreditScore(this.currentCreditScore)

Update(this.currentCreditScore)

 

5正向链接控制

    正向链接是一个非常强大的概念,它使原子规则能够组合成规则集,而无需定义规则间的依赖性,甚至不必知道这些依赖性。 但是,在某些情况下,规则编写者可能希望能够对链接行为提供更多控制,尤其是能够限制发生的链接。这使规则建模器能够做到以下几点:

·    限制规则的重复执行,从而避免得到不正确的结果。

·    提高性能。

·    防止出现失控循环。

 

    Windows Workflow Foundation 提供了下面的两个属性,以使这一级别的控制变得更加轻松:

·    RuleSet 上的 ChainingBehavior 属性。

·    每个 Rule 上的 ReevaluationBehavior 属性。

    两者的值都可在规则集编辑器中进行设置

    ChainingBehavior 属性

    RuleSet 对象上的 ChainingBehavior 属性可设置为三个可能的值:Full、UpdateOnly 或 None。

·    Full 选项为默认值,它提供了到目前为止所述的行为。

·    UpdateOnly 选项关闭隐式的、基于属性的链接,并规定链接只应对显式 Update 语句发生。这使您能够完全控制哪些规则引起重新计算。 通常,使用此选项可以避免导致规则过度(甚至是失控)重复执行的循环依赖性,或者通过消除为提供 RuleSet 的功能完整性所不需要的规则重新计算来提高性能。

·    最后一个选项为 None。 此选项使引擎以严格线性方式对规则进行计算。 每个规则都将按照优先级顺序分别只计算一次。优先级较高的规则可能影响优先级较低的规则,但反之则不然,因为不会发生链接。 因此,在使用此选项时,需要显式分配优先级,除非规则之间不存在依赖性。

   

    ReevaluationBehavior 属性

    Rule 对象上的 ReevaluationBehavior 属性有两个可能的值:Always 和 Never。

·    Always 为默认值,它提供了前面讨论过的行为,即,总是根据其他规则的操作所引起的链接重新计算规则。

·    Never 顾名思义就是关闭重新计算。 规则计算一次,但如果该规则先前已执行了任何操作,则不进行重新计算。换言之,如果先前计算过该规则,并因此执行了其 Then 或 Else 操作,则不会重新计算该规则。 但是,执行 Then 或 Else 操作中的空操作集合并不表示规则已经执行。

    通常,此属性在规则级别使用,目的是防止由规则对其自身操作的依赖性或对其他规则的依赖性造成的无限循环。例如,下面的规则会产生其自身的无限循环,并且无需进行重新计算即能满足该规则的功能要求:

IF this.shippingCharge < 2.5 AND this.orderValue > 100

THEN this.shippingCharge = 0

    另外如果打算重新计算该规则但只在 OrderValue 字段更改时重新计算则用户可以将 RuleSet 上的链接行为设置为只对显式 Update 语句进行链接然后将这些 Update 语句添加到相关的规则操作 当然,用户可能已经向此规则添加了附加谓词,以检查 ShippingCost 的值是否尚不为 0。但是,链接控制使用户无需根据计算过程的详细信息来定义其规则,而可以根据其业务要求来定义规则。

 

    Halt 函数

    作为最后一项控制,可以将 Halt 函数作为规则操作进行添加(在编辑器中的 Then 或 Else 操作框中键入Halt)。 这会立即停止 RuleSet 执行,并将控制返回给调用代码。 当然,此函数的用处并不局限于链接控制方案。 例如,一个具有特定功能目标的 RuleSet 可以使用 Halt 函数在达到目标后立即中断执行。

 

6以编程方式运行 RuleSet

    除了使用 PolicyActivity 活动运行 RuleSets 以外,您还可以在 RuleSet 中以编程方式运行和计算规则。 例如,假设您要在活动代码中依据活动(工作流或任何其他活动类型)执行 RuleSet。 下面的代码演示如何执行此操作:

 

RuleValidation validation = new RuleValidation(typeof(customActivity),null);

 

// "this" in the call below refers to the activity instance.

RuleExecution execution = new RuleExecution(validation, this);

customRuleSet.Execute(execution);

 

    一般情况下如果您在工作流中使用规则那么构造 RuleExecution 实例时还要传递 ActivityExecutionContext 提供上下文的主要优点是:您可以将规则执行信息发送到主机的跟踪提供程序。

    也可以使用 RuleEngine 对象直接执行 RuleSet 对象。 下面的代码演示如何执行此操作。

 

RuleValidation validation = new RuleValidation(typeof(customActivity),null);

 

// "this" in the call below refers to the activity instance.

RuleEngine engine = new RuleEngine(customRuleSet, validation);

engine.Execute(this);

 

    请注意如果只想根据给定类型例如如果它包含规则中引用的所有成员验证规则集则可以按如下所示进行表示

bool result = myRuleSet.Validate(validation);

 

    如果 RuleSet 有效 Validate 方法调用的结果为 true RuleValidation 实例具有一个存在验证错误的 Errors 属性。

注意

您也可以在工作流外使用 RuleSets 对于 RuleSet可根据任何 .NET Framework 类型进行编写。

 

7跟踪 RuleSet 信息

    执行 RuleSet 将向在宿主上配置的跟踪服务发送跟踪事件这些宿主已通过向其跟踪配置文件添加 UserTrack

抱歉!评论已关闭.