Evaluation of Rules


The evaluation of rules in a rule set is not a trivial process. There are many rules that dictate the execution path and reevaluation of rules based on changing properties. This evaluation process, also called chaining, is discussed in the following sections.

Chaining Options

Chaining describes the process of a rule’s evaluation related to other rules’ evaluations. The following options dictate how a rule set’s rules are evaluated in relation to other rules in the set:

  • Full chaining

  • Explicit chaining

  • Sequential (nonchained)

Full Chaining

Full chaining is a somewhat complex process of rule evaluation and reevaluation based on properties modified and rule priorities. Basically, the process goes something like this:

  1. Highest-priority rules are evaluated first.

  2. If a rule modifies the value of a property that a previously evaluated rule uses in its IF expression, the previous rule is reevaluated.

  3. This process continues until all rules have been processed or the HALT command is called.

Perhaps an example is the best way to explain full chaining and the different techniques with which it is implemented. Consider the following rules and their associated actions related to a car rental scenario:

Require Insurance, Priority = 15

IF:

  this.rental.CarType == CarType.Luxury && this.rental.Customer.Age <= 27 

THEN:

  this.rental.RequireInsurance = true 

Is Premium Customer, Priority = 10

IF:

  this.rental.Customer.Type == CustomerType.Premium 

THEN:

  this.rental.CarType = CarType.Luxury 

Gas Option, Priority = 5

IF:

  this.rental.GasOption == GasOption.RefillBeforeReturn 

THEN:

  this.rental.MinimumTankLevelUponReturn = this.rental.Car.CurrentTankLevel 

ELSE:

  this.rental.MinimumTankLevelUponReturn = 0 

Given these rules, assume the following facts:

  • The rental customer is 25 years old.

  • The customer opted to rent a Yugo (in other words, not a luxury car).

  • The customer is of Premium status.

  • The customer opts to refill the tank prior to returning the car.

The first rule to be evaluated is the Require Insurance rule because its priority, 15, is the highest of the three rules. Because the customer chose to rent a Yugo, this rule’s THEN expression is not executed even though the customer is under the age of 28. So far, nothing interesting has happened.

The next rule to be executed is the Is Premium Customer rule. This rule evaluates to true because the customer has achieved premium status. Because of this, the THEN expression is executed, thereby upgrading the rental to a luxury automobile.

Because the rental.CarType property was modified, and this property is read in the first rule, it must be reevaluated. Here’s where the chaining comes into play. Rather than the next rule’s being executed, the first rule is reevaluated. Now, because the rental car is considered luxury and the renter is only 25, the RequireInsurance flag is set to true.

After the Require Insurance rule has been evaluated the second time, and the insurance flag has been set, the final rule, Gas Option, is executed. The Is Premium Customer rule does not need to be evaluated a second time because the rental.Customer.Type property was not modified. At this point, because the customer has already opted to refill the tank himself prior to returning the vehicle, the rental.MinimumTankLevelUponReturn property is set to the current level of the car’s gas tank, which may have been retrieved from a database.

Because no other rules in this set rely on the MinimumTankLevelUponReturn property, the rule set’s execution is considered complete. As you can see, rule evaluation in the Windows Workflow Foundation rules engine is not as simple as 1-2-3. This is a good thing, of course, because it allows for powerful scenarios that otherwise would have to be implemented with custom code or a third-party package.

Implicit Chaining

The previous scenario used implicit chaining for rule evaluation and reevaluation. The workflow rules engine took it upon itself to investigate the properties each rule was inspecting and modifying. This is how the engine knew to reevaluate rules containing IF statements that might be affected by a value change in a subsequent rule.

Because there were no hints given to the rules engine related to what values might be looked at or modified, it had to make those assertions for itself. Hence, this method of chaining is called implicit chaining.

Attributed-Based Chaining

Quite often, the workflow engine cannot ascertain property dependencies across rules - such as when setting a property might modify the value of another property based on some logic in a class, or when a method call might do the same. For example, in the previous rental-car scenario, if calling a method named PerformCarUpgrade on a rental object modified the CarType property of the same object, the rules engine would have no way of knowing this. Therefore, you should provide hints to the rules engine so that the proper chaining behavior can take place. To do this, you use attributes defined in the workflow API, which are described in the following sections.

RuleRead

The RuleRead attribute tells the workflow engine that the property or method decorated with the attribute reads a specific property. For example, imagine if the Require Insurance rule’s IF statement looked like this:

  this.rental.GetCarType() == CarType.Luxury && this.rental.Customer.Age <= 27 

Now the rule is comparing a value returned from a method rather than accessing the car type value from a known property. Therefore, the definition of the GetCarType method needs to look like the following code to enable proper chaining:

  public class Rental {    private CarType _carType;    ...    [RuleRead("CarType")]    public CarType GetCarType()    {       return this._carType;    }    ... } 

This enables the engine to act as though the CarType property itself were read in the IF expression.

RuleWrite

Just as the RuleRead attribute is necessary for nonimplicit property inspections, the RuleWrite attribute allows methods and other properties to be flagged as member modifiers. For example, imagine that the Gas Option rule used a method called SetReturnGasLevel instead of directly setting the MinimumTankLevelUponReturn property. This would allow member fields and properties to be modified in such a way that more complex logic can be applied in methods and properties, and still keep the rules engine in the loop. The following code shows how you could modify the Rental object so that a method can be used to set the MinimumTankLevelUponReturn property:

  public class Rental {    // 1 would be a full tank    private decimal _returnTankLevel = 1;    public MinimumTankLevelUponReturn    {       get { return this._returnTankLevel; }    }    ...    [RuleWrite("MinimumTankLevelUponReturn")]    public void SetReturnGasLevel(decimal level)    {       // custom logic here...       this._returnTankLevel = level;    }    ... } 

Alternatively, you could use the set accessor of the MinimumTankLevelUponReturn property to implement this custom logic, but the example gets the point across. There are plenty of occasions where you can modify the fields or properties of a class indirectly in a method or another property.

Notice that in the examples so far, the attributes to assist the rules engine in chaining have been applied in custom classes rather than in a workflow’s code-beside code. This may not always be possible in the real world if classes were already defined during the original workflow development, or when you don’t want to place workflow-specific dependencies in their enterprise business classes. To mitigate these situations, you could either create class wrappers or create methods in a workflow class to call the class methods directly. The following is an example of this pattern:

  public class MyWorkflow : SequentialWorkflowActivity {    private Rental _rental;    ...    [RuleWrite("rental.MinimumTankLevelUponReturn")]    public void SetReturnGasLevel(decimal level)    {       this._rental.SetReturnGasLevel(level);    }    [RuleRead("rental.CarType")]    public CarType GetCarType()    {       return this._rental.GetCarType();    } } 

In this example, there is a class member variable of type Rental to contain business logic for a car rental. There are also methods in the workflow code-beside class that call Rental methods. Because these method calls read and write various values related to the rule, the MyWorkflow.GetCarType and MyWorkflow.SetReturnGasLevel methods are decorated with the appropriate rule attributes.

RuleInvoke

The RuleInvoke attribute tells the rules engine that the decorated method calls another method with the RuleWrite or RuleRead attribute applied. For example, the Rental object might have a method called PerformPremiumUpgrade, which among other things calls the SetReturnGasLevel method and passes a value of 0. This attribute brings together the other two chaining attributes to allow complete flexibility in code structure and workflow rules.

The following code listing is an example of what the PerformPremiumUpgrade method’s implementation might look like:

  public class Rental {    ...    [RuleInvoke("SetReturnGasLevel")]    public void PerformPremiumUpgrade()    {       ...       this.SetReturnGasLevel(0);       ...    }    ... } 

With this implementation, if you create a rule that looks like the following, the proper chaining path is followed when the rule set is evaluated:

IF:

  this._manualPremiumUpgradeSelected == true 

THEN:

  this._rental.PerformPremiumUpgrade() 

Explicit Chaining

The third and final method in which full chaining is implemented, explicit chaining, is the next natural progression after implicit and attribute-based chaining. Explicit chaining uses a rules-specific statement: Update.

The Update statement is called like a method and takes a path to a property that is said to be modified in the current rule. For example, if a method on a class modifies another value on that class, and you do not want to create method wrappers as shown previously, you can use explicit chaining to indicate that the value of interest is modified in a method call. The following example rule shows what this would look like in the car-rental scenario:

IF:

  this._rental.GasOption == GasOption.RefillBeforeReturn 

THEN:

  this._rental.SetReturnGasLevel(this._rental.Car.CurrentTankLevel); Update("_rental/MinimumTankLevelUponReturn") 

ELSE:

  this._rental.SetReturnGasLevel(0); Update("_rental/MinimumTankLevelUponReturn") 

As you can see, the Update statement passes a string representing a path to the member that was modified according to the actions taken in the THEN and ELSE statements. Note that just because the Update statement says that the MinimumTankLevelUponReturn property has been modified due to the code called in the THEN and ELSE statements does not necessarily mean that this is true. It is simply a flag that informs the rules engine to reevaluate any previous rules that rely upon that property in the IF statement. This is yet another way the rules engine provides complete control over rule evaluation.

You can also pass the Update statement a standard dotted path as opposed to the slash-delimited string. For example, you can express the Update call as follows (notice that the argument passed is not a string):

  Update(this._rental.MinimumTankLevelUponReturn) 

When you use the slash-delimited string method, the Update statement can also accept paths that include wildcards. For example, if a rules actions may cause every property of a given class to be updated, this can be represented in the following string:

  Update("_rental/*") 

If this statement is executed and full chaining is turned on, every rule that depends on a property of the _rental object is reevaluated. You can also use this slash-delimited property notation, including wildcards, with the RuleRead and RuleWrite attributes.

Explicit Chaining

The second option you can use to modify a rule set’s evaluation scheme is explicit chaining. This option has the same name as one of the full chaining methods, and they are definitely related - but they are also slightly different.

The explicit chaining discussed previously refers to the pattern of using Update statements to control chaining behavior. The explicit chaining evaluation discussed in this section refers to one of the three options you can apply to a rule set to modify its evaluation method. The explicit-chaining rule set option has a dependency on the Update method of chaining.

When a rule set’s chaining option is set to Full Chaining, explicit chaining is one of three ways in which chaining occurs. However, if a rule set’s chaining option is set to Explicit Chaining, the Update pattern is used exclusively. The Explicit Chaining rule set option tells the rules engine to follow only the evaluation paths defined by the explicit chaining pattern.

Sequential (Nonchained)

When a rule set’s chaining option is set to sequential, there is no chaining that occurs during evaluation. This means that rules are executed in order according to their priority (highest first), and no rules are reevaluated, regardless of any property dependencies.

The Rule Reevaluation Option

Each rule in a rule set has yet another option to control chaining behavior: the reevaluation option. The default value for a rule in the rules definition UI (covered later in this chapter) is Always, which means that a rule’s chaining behavior is consistent with the behavior described so far in this chapter.

The other possible value for the reevaluation option is Never. If a rule is set to never reevaluate, the rules engine honors this setting regardless of the chaining behavior specified for a rule or rule set.



Professional Windows Workflow Foundation
Professional Windows Workflow Foundation
ISBN: 0470053860
EAN: 2147483647
Year: 2004
Pages: 118
Authors: Todd Kitta

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net