WF学习系列之三:RuleSet知识点概述
注:学习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)。
在下面的概念性示例中,假定有下面的规则集,其中“A”、“B”等等表示工作流中的数据。
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 时,将向在宿主上配置的跟踪服务发送跟踪事件,这些宿主已通过向其跟踪配置文件添加 UserTrackPoint 注册了这些事件。 将发送 RuleActionTrackingEvent,其中提供了已计算的规则的名称以及条件计算结果 (true/false)。
通过跟踪活动执行情况,可以隐式地跟踪活动上的规则条件的计算结果。
启用跟踪
通过向应用程序配置文件中添加下面的代码,可以将其他的 RuleSet 计算信息发送给日志文件。
<configuration>
<system.diagnostics>
<switches>
<add name="System.Workflow.Activities.Rules" value="Information"/>
</switches>
</system.diagnostics>
</configuration>
将下列信息发送给日志文件:
· 规则集执行(信息)
· 条件计算结果(信息)
· 条件依赖项信息(详细)
· 操作副作用信息(详细)
· 链接关系(详细)
· 条件计算(详细)
· 操作执行(详细)
所有跟踪消息都是在指定的级别定义的,因此应根据需要查看的消息类型在配置文件中指定“信息”或“详细”级别。
8声明性 RuleSet
除了使用规则编辑器创建基于标记的规则集之外,还可以使用 RuleDefinitions 类以编程方式创建规则。此类具有两个名为 Conditions 和 RuleSets 的集合属性。
下面的示例演示如何以编程方式创建 RuleSet。 在此示例中,首先创建一个 RuleSet 对象,然后向其添加规则。 之后,创建引用表达式,以便将规则与用来实现 BuildRuleSet 方法的类中所显示的实例数据相关联。 discount、orderValue 和 customerType 引用是在该类中定义的字段。 下一步是使用 System.CodeDom 命名空间中的对象创建住户折扣规则。要创建的规则将检查定单值是否大于 500 且客户类型是否为住户。 如果条件为 true,则适用的折扣为 5%。 接下来,使用不同的值创建类似规则,指示如果客户为商业客户类型时所适用的折扣。这两个规则都将添加到初始 RuleSet 中。
private RuleSet BuildRuleSet()
{
RuleSet discountRuleSet = new RuleSet("DiscountRuleSet");
// Define property and activity reference expressions.
CodeThisReferenceExpression thisRef = new CodeThisReferenceExpression();
CodeFieldReferenceExpression discountRef = new CodeFieldReferenceExpression(thisRef, "discount");
CodeFieldReferenceExpression orderValueRef = new CodeFieldReferenceExpression(thisRef, "orderValue");
CodeFieldReferenceExpression customerTypeRef = new CodeFieldReferenceExpression(thisRef, "customerType");
CodeTypeReferenceExpression customerEnumRef = new CodeTypeReferenceExpression(typeof(CustomerType));
// Add the residential discount rule.
// IF OrderValue > 500 AND CustomerType = Residential
// THEN Discount = 5%
Rule resDiscountRule = new Rule("ResidentialDiscountRule");
discountRuleSet.Rules.Add(resDiscountRule);
// Define the first predicate test: OrderValue > 500.
CodeBinaryOperatorExpression resOrderValueTest = new CodeBinaryOperatorExpression();
resOrderValueTest.Left = orderValueRef;
resOrderValueTest.Operator = CodeBinaryOperatorType.GreaterThan;
resOrderValueTest.Right = new CodePrimitiveExpression(500);
// Define the second predicate test: CustomerType = Residential.
CodeBinaryOperatorExpression resCustomerTypeTest = new CodeBinaryOperatorExpression();
resCustomerTypeTest.Left = customerTypeRef;
resCustomerTypeTest.Operator = CodeBinaryOperatorType.ValueEquality;
resCustomerTypeTest.Right = new CodeFieldReferenceExpression(customerEnumRef, "Residential");
// Join the two predicates into a single condition.
CodeBinaryOperatorExpression resCondition = new CodeBinaryOperatorExpression();
resCondition.Left = resOrderValueTest;
resCondition.Operator = CodeBinaryOperatorType.BooleanAnd;
resCondition.Right = resCustomerTypeTest;
resDiscountRule.Condition = new RuleExpressionCondition(resCondition);
// Add the rule action: Discount = 5%.
CodeAssignStatement resDiscountAction = new CodeAssignStatement(discountRef, new CodePrimitiveExpression(5));
resDiscountRule.ThenActions.Add(new RuleStatementAction(resDiscountAction));
// Add the business discount rule.
// IF OrderValue > 10000 AND CustomerType = Business
// THEN Discount = 10%
Rule busDiscountRule = new Rule("BusinessDiscountRule");
discountRuleSet.Rules.Add(busDiscountRule);
CodeBinaryOperatorExpression busOrderValueTest = new CodeBinaryOperatorExpression();
busOrderValueTest.Left = orderValueRef;
busOrderValueTest.Operator = CodeBinaryOperatorType.GreaterThan;
busOrderValueTest.Right = new CodePrimitiveExpression(10000);
CodeBinaryOperatorExpression busCustomerTypeTest = new CodeBinaryOperatorExpression();
busCustomerTypeTest.Left = customerTypeRef;
busCustomerTypeTest.Operator = CodeBinaryOperatorType.ValueEquality;
busCustomerTypeTest.Right = new CodeFieldReferenceExpression(customerEnumRef, "Business");
CodeBinaryOperatorExpression busCondition = new CodeBinaryOperatorExpression();
busCondition.Left = busOrderValueTest;
busCondition.Operator = CodeBinaryOperatorType.BooleanAnd;
busCondition.Right = busCustomerTypeTest;
busDiscountRule.Condition = new RuleExpressionCondition(busCondition);
CodeAssignStatement busDiscountAction = new CodeAssignStatement(discountRef, new CodePrimitiveExpression(10));
busDiscountRule.ThenActions.Add(new RuleStatementAction(busDiscountAction));
return discountRuleSet;
}
若要创建一个 RuleDefinitions 对象并将它与某个工作流相关联,请按如下方式添加前面所显示的 RuleSet:
RuleDefinitions definitions = new RuleDefinitions();
definitions.RuleSets.Add(BuildRuleSet());
this.SetValue(RuleDefinitions.RuleDefinitionsProperty, definitions);
若要将 RuleSet 与 PolicyActivity 活动一起使用,请创建一个将 RuleSet 的名称用作构造函数参数的新 RuleSetReference,如下所示:
private void InitializeComponent()
{
this.CanModifyActivities = true;
// Create the Policy activity.
this.discountPolicy = new PolicyActivity();
this.discountPolicy.Name = "advancedDiscountPolicy";
this.discountPolicy.RuleSetReference = new RuleSetReference("DiscountRuleSet");
// Define the workflow.
this.Activities.Add(this.discountPolicy);
this.Name = "DiscountPolicyWorkflow";
this.CanModifyActivities = false;
}
private PolicyActivity discountPolicy;
使用下面的类似方法可以将规则用作活动条件:
// This code is called by the workflow constructor.
CodeBinaryOperatorExpression check = new CodeBinaryOperatorExpression();
check.Left = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "orderValue");
check.Operator = CodeBinaryOperatorType.LessThan;
check.Right = new CodePrimitiveExpression(10000);
RuleDefinitions definitions = new RuleDefinitions();
definitions.Conditions.Add(new RuleExpressionCondition("Condition1", check));
this.SetValue(RuleDefinitions.RuleDefinitionsProperty, definitions);
// This code is called in the InitializeComponent method to populate the
// condition on an IfElseBranch activity.
RuleConditionReference condition1 = new RuleConditionReference();
condition1.ConditionName = "Condition1";
ifElseBranch1.Condition = condition1;
9规则的编程注意事项
规则具有很强的可扩展性,并提供了许多功能;但是,关于规则编程,也有一些值得注意的方面。具体如下:
· 在规则操作内部更新的 struct 属性值不会在该规则之外反映出来。这是因为 struct 值是按值复制的,规则操作会修改副本,但不会修改 struct 中的原始值。
· 当将 decimal、double 或 float 数据类型强制转换为 int 数据类型时,值会向上舍入。这一行为与 C# 中的行为不同,在 C# 中,值会截断,而不是舍入。对值调用 Math.Ceiling 或 Math.Floor 将有效地截断值。
· 除非在单独的项目中定义泛型类型,并从工作流项目内部调用它,否则不支持在设计时使用泛型类型。
posted on 2008-12-10 09:14 mjgforever 阅读(2748) 评论(0) 编辑 收藏 举报