Share via


Introduction to the Windows Workflow Foundation Rules Engine

Jurgen Willis

Microsoft Corporation

November 2008 (updated March 2009)

Applies to:

  • Windows Workflow Foundation
  • Visual Studio 2008

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

Introduction

Windows Workflow Foundation (WF) provides rule capabilities to the .NET Framework development 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 Windows Workflow Foundation, Microsoft has been very focused on providing a seamless developer experience between workflow and rules modeling, so as to allow the developer to easily incorporate rules at any point in their workflow. The developer is 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, Windows Workflow 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 that allows 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 Windows Workflow Foundation 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 more about the rules functionality in Windows Workflow Foundation.

Overview of Rules in Windows Workflow Foundation

Rules technology is exposed in two key ways in Windows Workflow Foundation – as conditions on activities and as a ruleset in the Policy activity.

Activity Conditions

The following four activities that ship with WF use conditions:

  • 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 event 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 below.

 

Figure 1: WF Rule Condition Authoring

Adding a Rule Condition

These are the steps involved in adding a rule condition to an IfElseBranch activity which 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.

The "this" operator can be used in conditions to refer to the workflow and access fields or properties defined in the workflow class. 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, e.g. "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.

The relational operators supported in expressions are:

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

In addition, the following arithmetic operators may be used:

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

Expressions can be combined/negated using:

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

In addition to these operators, conditions can also use the "new" keyword to create instances of .NET types. This allows developers to create an instance of an object to reference properties or invoke methods as part of the condition evaluation, without requiring the workflow to contain a property or field of that type. For example, a condition could use code like the following:

this.orderValue > 100 && new OrderManager(this.orderID).IsExistingCustomer

The primary driver for a developer to use a Rule Condition instead of 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, etc.

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 below and is analogous to that seen with rule conditions above.

 

Figure 2: WF RuleSet Authoring

Adding a Policy Activity

These are the steps required to configure a RuleSet for the policy activity.

  1. The policy activity is dragged from the toolbox onto the workflow designer.
  2. In the RuleSetReference property 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 using the "this" keyword.
  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 inFigure 3.

 

Figure 3: WF RuleSet Browser

Rules Evaluation

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

The evaluation mechanism can be conceptually described as:

  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 ruleset below, where "A", "B", etc. 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; Rules 3 and 2 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 above 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. When the actions of a rule change a value used in a previously evaluated rule condition, that condition is re-evaluated and the corresponding actions executed. These dependencies between rule actions and conditions 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, in most instances be blissfully unaware of the chaining or the need for it. 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 in a .NET class 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:

Rule 1

IF this.discount > 0

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

Rule 2

IF this.subtotal > 10000

THEN this.SetDiscount(.05)

The SetDiscount method could then be attributed as follows, which would allow the engine to identify that Rule 1 is dependent on Rule 2 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 1's action 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 order;

     ...

}

Execution of Rule 2 below, 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, thus the reference in the path to the "order" field in the workflow). 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 – e.g. 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, e.g. 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 via 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 where 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 where 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 where 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, e.g.

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.

There are two properties provided in WF Rules that facilitate this level of control:

  • 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 that is 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. It is important to note, however, that execution of an empty action collection in the Then or Else actions will NOT mark a rule as having been executed.

Typically this property would be used to – at the rule level – prevent infinite looping due to dependencies 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. 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 a user desires 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 the user 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.

Sequencing can also be valuable if the rules have dependencies, but the user does not want to use explicit Update statements or method attributing for scenarios where they would otherwise be required.

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 host who have registered for these events by adding a UserTrackPoint to their tracking profile. The matching locations for the UserTrackPoint should include the Policy activity type and an ArgumentType of RuleActionTrackingEvent. A RuleActionTrackingEvent is sent on the UserData property of the UserTrackingRecord, 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 and using a tracking profile that specifies data extractions. See the references section for links to more information on  tracking services.

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="System.Workflow LogToFile" value="1" />

        <add name="Rules" value="Verbose"/>

      </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.

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

Custom rule execution and authoring

In addition to executing RuleSets via the policy activity, developers can program against the rules API to take control of the execution. This code can take the form of a custom activity or may be executed outside of workflows all together. The following code shows the simplest code to execute a RuleSet against an object.

void  RunRulesOnOrder(Order o)

{

RuleSet rules = GetRules(rulesName);

RuleEngine engine = new RuleEngine(rules, typeof(Order));

engine.Execute(o);

}

In this sample, the rules are being loaded from an external source which can be a database, the file system, or some other store. The RuleSet object can be serialized and deserialized using the WorkflowMarkUpSerializer class as shown here.

WorkflowMarkupSerializer serializer = new WorkflowMarkupSerializer();

RuleSet ruleset = (RuleSet)serializer.Deserialize(

XmlReader.Create("MyRuleSet.xml"));

The rules are executed against a .NET object and the rules must be defined against the type of that object. When editing rules using the policy activity, the RuleSet editor is created and initialized with the type of the workflow. Custom .NET applications can also use the RuleSet editor dialog to aid in authoring rules. By creating an instance of the editor and initializing it with a .NET type; the editor can be used to define rules using that type. The "this" reference in the rules references the type that the rules are being built against. The code below shows how to initialize the editor dialog to create a new RuleSet and retrieve the authored rules.

RuleSetDialog dialog = new RuleSetDialog(typeof(Order), null, null);

if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)

{

RuleSet rules = dialog.RuleSet;

//serialize rules

}

Once the rules are returned from the dialog they can be serialized using the WorkflowMarkupSerializer and saved. Using the editor to author rules and the RuleEngine class to execute them allows rules to be executed both in the context of workflows and activities or in standalone .NET code. For a detailed example of custom authoring and external storage of rules, see the External RuleSet Toolkit sample in the references. The sample also contains a custom activity implementation which executes the RuleSet using the techniques shown here.

Summary

Windows Workflow Foundation provides a flexible rules capability which 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 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

  • 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)
  • ChangingRules – demonstrates the use of the dynamic update APIs to change a RuleCondition on a running workflow instance (\Technologies\DynamicUpdate)
  • 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)
  • External RuleSet Toolkit – provides an example of how to externalize rules from workflow assemblies(Technologies\Rules and Conditions)

MSDN Workflow Forum

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

About the Author

Jurgen Willis is now the Principal Program Manager, managing program management for Windows Workflow Foundation (WF) technology. For WF 3.0, Jurgen was the program manager responsible 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.

This article was originally written in February, 2006; and updated in November, 2008 by CSD MVP Matt Milner.