Introduction to the Windows Workflow Foundation Rules Engine

 

Jurgen Willis
Microsoft Corporation

February 2006

Applies to:
   Windows Workflow Foundation (WF) Beta 2
   Visual Studio 2005

Summary: This article provides an overview of the rules engine capabilities in Windows Workflow Foundation (WF). It describes how conditions and RuleSets are used in WF and discusses the behavior of collections of rules, including forward chaining and tracking and tracing. (18 printed pages)

Note   This article was written using beta 2 and in the future some changes may need to be made to get this to work on later versions of Windows Workflow Foundation.

Contents

Introduction
Overview of Rules in Windows Workflow Foundation
Rules Evaluation
Forward Chaining
Forward Chaining Control
Additional Modeling Discussion
Tracking and Tracing
Conclusion
For More Information

Introduction

With the availability of Windows Workflow Foundation (WF), Microsoft is introducing new rules capabilities to the WinFX developer platform. These capabilities extend from simple conditions that drive activity execution behavior all the way up to complex RuleSets executed by a full-featured forward chaining rules engine.

The rules capability allows for the declarative modeling of units of application logic within the scope of an overall business process. Sample scenarios for rules engine technology are order validation, pricing calculation, promotion enforcement, exception process management, and claims adjudication and management.

A key objective in developing this technology was to provide a truly integrated workflow and rules experience. Across the industry, rules and workflow have typically been quite distinct technologies, usually provided by different vendors. Third-party rules engines are often embedded or integrated by workflow and Business Process Management (BPM) providers, but the developer and administration experience is clearly not a seamless one.

With WF, Microsoft has been very focused on providing a seamless developer experience between workflow and rules modeling, so as to allow developers to easily incorporate rules at any point in their workflow. Developers are able to make the determination of whether to model their logic in the workflow model, rules, or code without having to worry about the integration implications of those decisions. This has been achieved, however, without sacrificing the ability to execute rules outside the scope of a workflow.

A second key objective has been to provide a straightforward model for developers to use in defining rules. Rules technology has heretofore often been a niche technology, used by a highly specialized group of rules professionals. While improvements in tooling have broadened the set of individuals using rules, the models have often required users to be too well versed in the implementation mechanics of the underlying engine. In providing rules capabilities into the platform, we have opted for a model that will be approachable by the broad .NET developer community, and ultimately non-developers. While there will always be a certain level of domain expertise—as with any technology—our goal is for all .NET developers to be able to quickly ramp up on the rules model and easily incorporate it into their applications.

In addition to providing an approachable model, WF also provides a powerful evaluation engine to support complex rules scenarios, demanding forward chaining evaluation and precise evaluation control. This is delivered in a manner that provides a number of extensibility points, allowing developers to build on our platform and deliver rules capabilities in support of a wide spectrum of scenarios.

This document provides a technical introduction to the rules capabilities delivered in WF and an overview of the available features and their use. At the end of this document is a list of additional resources that can be used to learn about the rules functionality in WF.

Overview of Rules in Windows Workflow Foundation

Rules technology is exposed in two key ways in WF—as conditions on activities and as a forward chaining RuleSet in the Policy activity. Forward chaining will be discussed later in the document; briefly put, it refers to the ability for the actions of one rule to cause other, dependent rules to be reevaluated.

Activity Conditions

Conditions are used by the following four activities that ship with WF:

  • IfElseBranch
  • While
  • Replicator
  • ConditionedActivityGroup (CAG)

Conditions are used to drive the execution behavior of these activities—for example, determining whether a given IfElseBranch will execute. Conditions can be specified either as CodeConditions, which will have a configured handler in the code beside, or as a RuleConditionReference. A RuleConditionReference will point to a RuleCondition definition in a .rules file associated with the workflow in the workflow project. The experience in Visual Studio is seen in Figure 1.

Aa480193.intwf_fndrlseng01(en-us,MSDN.10).gif

Figure 1. WF Rule Condition Authoring

Adding a Rule Condition

These are the steps involved in adding a rule condition to an IfElseBranch activity that sits inside the IfElse activity:

  1. When an activity with a condition is selected (as with the IfElseBranch activity shown) a Condition property is shown in the Properties grid.
  2. A drop-down for the Condition property allows the user to pick a CodeCondition or a RuleConditionReference.
  3. If RuleConditionReference is selected, the Condition property can be expanded to see the ConditionName and Expression properties.
  4. After providing a condition name, the ellipses in the Expression property are selected to launch the Rule Condition Editor. This editor allows the developer to type in the condition in text form, with intellisense-like support. The text is parsed into the associated object model representation of the RuleCondition.
  5. After selecting OK, the RuleCondition definition is serialized into a <WorkflowName>.rules file that is added to the project.

To reference fields or properties on the workflow, you can type this. into the editor. After the dot is typed, an intellisense-like menu appears that allows you to pick the members on the workflow (you can also just type the member in directly). Nested calls can be made as well; for example, this.order.Total. Static methods can be called on referenced types by typing in the class name followed by the method name, as you would in code.

Expressions are supported with the following relational operators:

  • Equal ("==" or "=")
  • Greater than (">")
  • Greater than or equal (">=")
  • Less than ("<")
  • Less than or equal ("<=")

In addition, you may use the following arithmetic operators:

  • Add ("+")
  • Subtract ("-")
  • Multiply ("*")
  • Divide ("/")
  • Modulus ("MOD")

Expressions can be combined/negated using the following operators:

  • AND ("AND", "&&")
  • OR ("OR", "||")
  • NOT ("NOT", "!")
  • Bitwise and ("&")
  • Bitwise or ("|")

The primary reason for a developer to use a Rule Condition instead of a Code Condition is that Rule Conditions become part of the model and can be dynamically updated at runtime on executing workflow instances. A secondary advantage of Rule Conditions is that, as part of the model, more sophisticated tools could be built on top of the model to provide additional authoring experiences, dependency management, cross-condition analysis, and so on.

Policy Activity

The Policy activity encapsulates the definition and execution of a RuleSet. A RuleSet is a collection of rules with a set of execution semantics. Rules are, in turn, If-Then-Else expressions that operate on the workflow members. The experience is shown in Figure 2 and is analogous to that seen with Rule Conditions.

Aa480193.intwf_fndrlseng02(en-us,MSDN.10).gif

Figure 2. WF RuleSet Authoring

Adding a Policy Activity

To configure a ruleset for the policy activity follow these steps:

  1. Drag the policy activity from the toolbox onto the workflow designer.
  2. In the RuleSetReference a name is provided for the RuleSet.
  3. Selecting the ellipses in the RuleSet Definition field launches the Rule Set Editor.
  4. The editor shows a collection of rules in a list box.
  5. Selecting a given rule shows its Condition, Then actions, Else actions, and a set of additional properties.
  6. As in the Rule Condition Editor, the Condition, Then actions, and Else actions are entered in as text and parsed into the associated object model representation; rules are built against the members on the workflow.
  7. Selecting OK serializes the RuleSet to the .rules file associated with the workflow.

Note that selecting the ellipses in the RuleSetReference property will launch the editor that allows the user to select an existing RuleSet or add/rename/remove RuleSets as demonstrated in Figure 3.

Aa480193.intwf_fndrlseng03(en-us,MSDN.10).gif

Figure 3. WF RuleSet Browser

Rules Evaluation

Each rule in a RuleSet has a priority value with a default of 0. The rules in a RuleSet can be considered a sorted collection, ordered by the priority values. The WF Rules evaluator evaluates rules individually and executes the rule's actions based on the results of the rule's condition evaluation.

The evaluation mechanism can be conceptually described with the following procedure:

  1. Start with the list of active rules.
  2. Find the highest priority rule.
  3. Evaluate the rule and execute its Then/Else actions as appropriate.
  4. If the actions of a rule update a field/property that is used by a previous rule in the list (one with a higher priority), reevaluate that previous rule and execute its actions as appropriate. Note that only those rules with a specific dependency are reevaluated.
  5. Continue the process until all rules in the RuleSet have been evaluated.

Let's look at a simple example. Assume that you have the following RuleSet, where A, B, and so on, represent data on the workflow.

Rule4 (Priority = 4)

IF A = 15
THEN B = 5

Rule3 (P=3)

IF C = 5
THEN B = 10

Rule2 (P=2)

IF D = 2
THEN A = 15

Rule1 (P=1)

IF B = 5
THEN E = 7

Assume that you have the following input data:

  • A = 0
  • B = 0
  • C = 5
  • D = 2
  • E = 0

The evaluation would proceed as follows:

  • Rule4 is evaluated; it evaluates to false, and since it has no Else actions, no actions are executed.
  • Rule3 evaluates to true and its action is executed, setting B = 10. Rule4 does not depend on the value of B, so the evaluation proceeds to Rule2.
  • Rule2 evaluates to true and its action is executed, setting A = 15.
  • Since Rule4 uses the value of A in its conditions, it is reevaluated. It evaluates to true and its action is executed, setting B = 5; Rule3 and Rule2 are not reevaluated since their conditions do not rely on the value of A. No previous rules depend on the value of B, so the evaluation proceeds to Rule1.
  • Rule1 evaluates to true and its action is executed, setting E = 7.

The resulting data set would now be:

  • A = 15
  • B = 5
  • C = 5
  • D = 2
  • E = 7

Forward Chaining

As seen previously, chaining is based on the identified dependencies among rules; more specifically, the dependencies among the actions of a rule and the conditions of other rules. These dependencies can be identified or declared in one of three ways:

  • Implicit
  • Attribute-based
  • Explicit

Implicit

Implicit dependencies are identified automatically by the engine. When a RuleSet is first executed, each rule is analyzed to assess the fields/properties that it reads in its condition and writes to in its actions. This is done by walking the expression in the condition and the statements in the actions. For example, assume the following rules.

Rule 1

IF this.subtotal > 10000
THEN this.discount = .05

Rule 2

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

The engine would evaluate the rules and identify that Rule 1 reads the subtotal field and writes to the discount field. Rule 2 reads the discount and subtotal fields and writes to the total field. Therefore, Rule 2 has a dependency on Rule 1; the result is that the engine will ensure that Rule 2 is evaluated/reevaluated whenever Rule 1 executes its Then action.

Note that dependencies are identified at the leaf node level. Take the following RuleSet.

Rule 1

IF this.order.Subtotal > 10000
THEN this.order.Discount= .05

Rule 2

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

Rule 3

IF this.order.CustomerType = "Residential"
THEN ...

A dependency would still be identified between Rules 1 and 2. However, Rule 3 would not have a dependency on either Rule 1 or 2, since neither updates the CustomerType property. In other words, the dependency is identified at the level of the CustomerType property, not the Order object itself.

Through implicit chaining, WF does the majority of the necessary chaining for the user, so that the user does not have to explicitly model updates, and can, in fact, be blissfully unaware of the chaining or the need for it, in most instances. We expect that this will make the modeling of complex RuleSets more straightforward for most users and make the notion of chaining a powerful, but mostly hidden, capability of the engine. However, the two other mechanisms to drive chaining, attribute-based and explicit, are provided for more complex and specific scenarios.

Attribute-Based

For method calls within a rule, it becomes more difficult to deterministically evaluate the reads/writes that occur. To address this problem, WF provides three attributes that can be applied to a method to indicate its actions:

  • RuleRead
  • RuleWrite
  • RuleInvoke

Attributing a method with the RuleRead attribute indicates that it reads the indicated property. Similarly, the RuleWrite attribute can be used to indicate that a method updates a given field or property. Assume that our first two rules under the implicit section were rewritten as the following.

Rule 1

IF this.subtotal > 10000
THEN this.SetDiscount(.05)

Rule 2

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

The SetDiscount method could then be attributed as follows, which would allow the engine to identify that Rule 2 is dependent on Rule 1 due to the use of the discount field.

[RuleWrite("discount")] 
void SetDiscount(double requestedDiscount)
{
     ...//some code that updates the discount field
}

The RuleInvoke attribute can be used to dictate dependencies due to linked method calls. For example, assume the following modification to the rules and methods.

Rule 1

IF this.subtotal > 10000
THEN this.SetDiscountWrapper(.05)

Rule 2

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

[RuleInvoke("SetDiscount")]
void SetDiscountWrapper(double requestedDiscount)
{
     ...
     SetDiscount(requestedDiscount);
     ...
}

[RuleWrite("discount")] 
void SetDiscount(double requestedDiscount)
{
}

Rule 2's condition calls SetDiscountWrapper, which in turns calls SetDiscount, which writes to the discount field. The RuleInvoke attribute allow this indirect write to be declared and detected by the engine.

It is important to recognize that the field or property referenced in the attribute path refers to a field or property on the same class as the method, which is not necessarily the root object passed to the RuleSet for execution. For example, you might attribute the Order class as follows.

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
     }
}

You could then use an instance of this class in a workflow as follows.

public class Workflow1 :  SequentialWorkflowActivity
{
     private Order discount;

     ...
}

Execution of Rule 2, then, would cause reevaluation of Rule 1.

Rule 1

IF this.order.Discount > 5
THEN ...

Rule 2

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

A few additional points on the use of attributes:

  • Attributes point to the fields/properties on the Owner class, not the parameters to a method call.
  • Wildcards can also be used in the rule attributes. For example, RuleWrite("order/*") could be used to indicate that all fields on the object referenced by the "order" field are modified by the method (in this example, the method would be on the workflow itself, not the Order class). The wildcard can only be used at the end of the path, though; an attribute such as RuleWrite("*/Discount") is invalid.
  • An attribute such as RuleWrite("order") can be used with reference types to indicate that the reference has changed—for example, to indicate that the variable now points to a different Order instance. All rules that use a field/property on the variable will be assumed to be impacted, in addition to all rules that test the instance reference itself—for example, IF this.order == this.order2.
  • The default behavior if no method attributes are specified is that a method call is assumed to read all fields/properties on the target object (the object on which the method is invoked) but write to none of them. Furthermore, a method is assumed to read from the parameters that are passed to it, but is assumed to not write to any of them. Out and ref parameters are assumed to be written to when used in rule actions.

Explicit

The final mechanism for indicating field/property dependencies is through the use of the Update statement. The Update statement takes as its argument either a string that represents the path to a field or property or an expression that represents a field/property access. For example, either of the following two statements could be typed into the RuleSet editor to create an Update statement on the Name property of a Customer instance on the workflow.

Update("this/customer/Name") 

OR

Update(this.customer.Name)

The Update statement indicates that the rule writes to the indicated field/property. This would have the same effect as a direct set of the field/property in the rule or the calling of a method with a RuleWrite attribute for the field/property.

The Update command also supports the use of wildcards. For example, you could add the following Update command to a rule.

Update("this/customer/*")

This would cause any rules that use any property on the Customer instance in their conditions to be reevaluated. In other words, each of the following two rules would be reevaluated.

IF this.customer.ZipCode == 98052
THEN ...

IF this.customer.CreditScore < 600
THEN ...

Generally, it is not expected that users will need to model explicit Update statements in most scenarios. The implicit chaining should provide the required dependency analysis and chaining behavior in most scenarios. Method attributing should support the most prevalent scenarios in which implicit chaining cannot identify the dependencies. In general, method attributing would be the preferred method for indicating dependencies over the use of the Update statement, since the dependency can be identified once on the method and leveraged across many different rules that use that method. Also, in scenarios in which the rule writer and method implementer are different individuals (or the work is done at different times), method attributing allows the method writer—who knows the code best—to identify what the dependencies are of the method.

However, there will be some scenarios in which the Update statement will be the appropriate solution, such as when a field/property is passed to a method on a class that the workflow writer does not control and therefore cannot attribute, as is the case in the following code.

IF ...
THEN this.customer.UpdateCreditScore(this.currentCreditScore)
     Update(this.currentCreditScore)

Forward Chaining Control

Forward chaining is a very powerful notion that allows atomic rules to be assembled into RuleSets without the definition of, or necessarily even the knowledge of, the dependencies among the rules.

However, in some scenarios, the rule writer may want the ability to provide more control over the chaining behavior, specifically the ability to limit the chaining that takes place. This enables the rule modeler to:

  • Limit the repetitive execution of rules, which may give incorrect results.
  • Increase performance.
  • Prevent runaway loops.

This level of control is facilitated in WF rules by these two properties:

  • A Chaining Behavior property on the RuleSet.
  • A Reevaluation Behavior property on each rule.

Both of these values can be set in the RuleSet Editor.

Chaining Behavior Property

The Chaining Behavior property on the RuleSet has three possible values:

  • Full Chaining
  • Explicit Chaining
  • Sequential

The Full Chaining option is the default and provides the behavior described up to this point. The Explicit Chaining option turns off the implicit and attribute-based chaining and prescribes that chaining should only occur for explicit Update statements. This gives the rule writer complete control over what rules cause reevaluation. Typically this would be used to either avoid cyclic dependencies that cause excessive (or even runaway) rule re-execution, or to boost performance by eliminating superfluous rule reevaluation not required to provide functional completeness of the RuleSet.

The final option is Sequential. This option will cause the engine to evaluate the rules in strictly linear fashion. Each rule would be evaluated once and only once and in the order of priority. Rules with a higher priority could impact rules with lower priorities, but the inverse would not be true since no chaining would occur. Therefore, this option would be used with explicit priority assignments unless no dependencies existed among the rules.

Reevaluation Behavior Property

The Reevaluation Behavior on the rule has two possible values:

  • Always
  • Never

Always is the default and provides the behavior previously discussed, namely that the rule will always be reevaluated based on chaining due to the actions of other rules. Never, as the name implies, turns off this reevaluation. The rule will be evaluated once but will not be reevaluated if it has previously executed any actions. In other words, if the rule has previously been evaluated and consequently executed its Then or Else actions, it will not be reevaluated. Execution of an empty action collection in the Then or Else actions will not mark a rule as having been executed, though.

Typically this property would be used—at the rule level—to prevent infinite looping due to dependencies that the rule has, either on its own actions or other rules. For example, the following rule would create its own infinite loop and reevaluation is not required to fulfill the functional requirements of the rule.

IF this.shippingCharge < 2.5 AND this.orderValue > 100
THEN this.shippingCharge = 0

Alternatively, if the rule is intended to be reevaluated—but only if the OrderValue field is changed—then the user can set the chaining behavior on the RuleSet to only chain on explicit Update statements (and then add those Update statements to the relevant rule actions). Of course, the user could have added an additional predicate to this rule that checks that the value of ShippingCost is not already 0, but the chaining controls remove the need for users to define their rules based on the evaluation details, as opposed to their business requirements.

Halt Function

As a final control, a Halt function can be added as a rule action (simply type "Halt" into the Then or Else action boxes in the editor). This will immediately stop RuleSet execution and return control to the calling code. The usefulness of this function is not necessarily limited to chaining control scenarios, of course. A RuleSet with a specific functional goal, for example, may use a Halt function to short-circuit execution once the goal has been reached.

Additional Modeling Discussion

Priority-based Execution

As mentioned in a previous section, if users desire a certain execution sequence for the RuleSet or a subset of rules in that RuleSet, they can precisely define this sequence using the priority field on a rule. Doing so often removes the requirement for chaining, and users can even turn off the chaining in these scenarios. We have found that in many cases rule dependencies can be met simply by providing explicit execution sequencing.

It is important to note, though, that the forward execution mechanism in WF provides a capability for users to define an execution sequence, but does not require them to do so. In most cases, the forward chaining behavior makes the assignment of rule priorities unnecessary to achieve the correct RuleSet result, since relationships are automatically managed by the engine to ensure that rule dependencies are met.

Take the following rules.

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"

In WF, you can provide a higher priority on Rule 1 so that it executes first. This ensures that Drink.Style is set before Rule 2 is evaluated.

Sequencing is not required, however, to get the desired results. Assume that Rule 2 was evaluated first. In this case, the Drink.Style may be null or could be another style. This would result in the Snack.Style being set to Muffin. However, after Rule 1 executes and sets the Drink.Style to Latte, Rule 2 would be reevaluated and would set the Snack.Style to Scone. Essentially, the user has the option of dictating sequencing, but in many cases does not need to do so.

Collection Processing

In some scenarios, you may need to evaluate rules against all items individually in a collection. Iterating over the collection can be done in a number of ways, but one way is with a rule pattern such as this:

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")

Tracking and Tracing

Tracking

When a RuleSet executes, tracking events are sent to tracking services configured on the hosts who have registered for these events by adding a UserTrackPoint to their tracking profile. A RuleActionTrackingEvent is sent, which provides the name of the rule that was evaluated, as well as the condition evaluation result (true/false). See the RuleActionTrackingEvent Sample in the SDK for an example.

The evaluation results of rule conditions on activities can be implicitly tracked by tracking activity execution.

Tracing

Additional RuleSet evaluation information can be sent to a log file by adding the following to an application configuration file.

<configuration>
   <system.diagnostics>
      <switches>
           <add name="Rules" value="Information"/>
      </switches>
   </system.diagnostics>
</configuration>

The following information will be sent to the log file:

  • Condition dependency information:
    • Example: Rule "ReturnNumberOfStops" Condition dependency: "this/currentFlight/OutboundFlight/NumberOfStops/"
  • Action side-effect information:
    • Example: Rule "ReturnNumberOfStops" THEN side-effect: "this/currentFlight/Score/"
  • Chaining relationship:
    • Example: Rule "ReturnNumberOfStops" THEN actions trigger rule "ApprovedFlights"
  • RuleSet execution:
    • Example: Rules: "Executing RuleSet FlightRuleSet"
  • Condition evaluation:
    • Example: Rules: Evaluating condition on rule "SetDefaultScore"
  • Condition evaluation result:
    • Example: Rules: Condition evaluated to True
  • Action execution:
    • Example: Rules: Evaluation THEN actions for rule"SetDefaultScore"

All tracing messages are currently defined at the "Information" level, so you should specify a level of Information or Verbose in your configuration file to see rules tracing.

Conclusion

WF provides a flexible rules capability that can be leveraged in many different ways to support a broad range of scenarios. From simple activity conditions to sophisticated forward chaining RuleSets, the technology allows you to seamlessly integrate rules support into your workflows. Additionally, the rules engine can also be leveraged outside of workflows to provide rules functionality to any .NET application.

The feature set allows new developers to easily incorporate simple rules into workflows, while still providing the richness and extensibility to support much more advanced RuleSets and application scenarios. This document, combined with the resources cited in the next section, should help developers new to WF Rules learn about the technology and quickly become productive with its use.

For More Information

  • MSDN Workflow Site

    Numerous documents and links to webcasts and labs.

  • Community Site Samples

    • External RuleSet Toolkit—provides an example of how to externalize rules from workflow assemblies.
    • RuleSet Analysis—a tool to analyze RuleSets for relationships among rules and between rules and data.
    • Rules in Excel—provides an example of how to use rules standalone, outside of a workflow. Also demonstrates how to programmatically create rules that are authored via a decision table in Excel.
  • SDK Samples

    • IfElseWithRules—shows the use of a RuleCondition on an IfElse activity (under \Technologies\RulesAndConditions).
    • DynamicUpdateChangingRules—demonstrates the use of the dynamic update APIs to change a RuleCondition on a running workflow instance (\Technologies\RulesAndConditions).
    • SimplePolicy—demonstrates the use of the APIs to define a simple RuleSet and policy activity (\Technologies\Activities\Policy).
    • AdvancedPolicy—defines a more complex RuleSet (\Technologies\Activities\Policy).
    • RuleActionTrackingEventSample—shows how to capture rule evaluation results in a tracking provider (\Technologies\Tracking).
  • MSDN Workflow Forum

    • For questions related to WF Rules or WF in general, please visit this discussion forum.

 

About the author

Jurgen Willis is a Program Manager on the Windows Workflow Foundation team with responsibility for rules engine technology and rules-driven activities. Prior to joining Microsoft, Jurgen designed and implemented integration and process management solutions for Fortune 500 companies.