Monday, June 28, 2004

Watch your type

A simple rule:
Conditions:

Case:/Root/Income/BasicSalary
is less than or equal to
75.000
Actions:
Case:/Root/IncomeStatus = approved

Would a basic salary of only 800 be approved? Or rephrasing: would the rule fire?
I expected it would. But the answer is "It depends". It depeneds on the type of BasicSalary! If the BasicSalary is of type System.String it will not!
CONDITION EVALUATION TEST (MATCH) 6/28/2004 12:29:10 PM

Rule Engine Instance Identifier: 29fea928-...
Ruleset Name: Simple
Test Expression: TypedXmlDocument: Case:/Root/Income.BasicSalary <= 75.000
Left Operand Value: 800
Right Operand Value: 75.000
Test Result: False

It seems that the rule engine allows string comparison. And in case of string comparison "800" would be larger than "75.000"! So watch your types! An interesting item to explain to the rule writer!

What about converting the BasicSalary to an Int32?
I tried to add a .NET class in the Business Rules Composer. Selected the mscorlib to add as an assembly and changed the condition to:
Conditions:

Int32.Parse(Case:/Root/Income/BasicSalary)
is less than or equal to 75.000
Actions:
Case:/Root/IncomeStatus = approved


Don't know how to handle exceptions here, but let's see what happens:
I reloaded the policy, and mysteriously the 75.000 value was changed to 75! But hey, they also changed the type from System.String to System.Double. Close, but no cigar.

Should I put in 75,000? No: that got interpreted as an error where a string can not be compared with an Int32. And the policy could not be saved.

Let's continue and remove any thousand separator:
Conditions:

Int32.Parse(Case:/Root/Income/BasicSalary)
is less than or equal to 75000
Actions:
Case:/Root/IncomeStatus = approved

Reloading the policy and we notice that the 75000 literal has type System.Int32. So we got the right type in place now.

Enterering the next stage: Testing if the rule actual fires. We can not define an instance of System.Int32 because of a missing interface. We need a fact creator. Although the Int32.Parse function is just a static method, we still have to assert the fact in the rule engine. Testing without the fact creation results in no rule-firing.

Hang in there, we are getting close. We only have to create a helper class that implements the Microsoft.RuleEngine.IFactCreator (located in the Microsoft.RuleEngine.dll)

namespace MarcoSoft.Utils
{
///
/// Summary description for Converter
///

public class Converter: Microsoft.RuleEngine.IFactCreator {
public Converter() {
}

public int stringToInt32(string value){
return Int32.Parse(value);
}

#region IFactCreator Members
public object[] CreateFacts(RuleSetInfo ruleSetInfo) {
return new object[]{new Converter()};
}

public Type[] GetFactTypes(RuleSetInfo ruleSetInfo) {
Type converterType = this.GetType();
return new Type[]{converterType};
}
#endregion
}
}


This code has to be compiled with a strong name, and be placed in the Global Assembly Cache. On the Facts Explorer view of the MS Business Rule Composer we can add the created .NET assembly.
We update the condition with:
Conditions:

Converter.stringToInt32(Case:/Root/Income/BasicSalary)
is less than or equal to 75000
...

And in the Test Dialog we add a Fact.Creator of the MarcoSoft.Utils.Converter
Running the code, and voila:

CONDITION EVALUATION TEST (MATCH) 6/28/2004 5:22:18 PM
Rule Engine Instance Identifier: 52c89cd5-...
Ruleset Name: Simple
Test Expression: MarcoSoft.Utils.Converter.stringToInt32 <= 75000
Left Operand Value: 800
Right Operand Value: 75000
Test Result: True

The result what I was expecting all along.

4 comments:

kandachar said...

if i use without the .net function i get the condition tested

If i use the .net method i am not getting desired result.

I have installed the assembly into GAC
Where i am going wrong ?

Ensing said...

Kandachar,

It's difficult for me to guess what exactly you are doing and what might be going wrong. Can you elaborate a bit more on your situation?

When you say "I get the condition tested", do you mean that the evaluation of the expression is correct?

When using the .Net type converter function, do you create an instance of the Converter with the Business Rule Composer?

Marco

Anonymous said...

I am trying to call a .NET method as explained here from the composer but I just cannot see it coming up in the evaluation trace.

Ensing said...

Priya,

You did create the IFactCreator? and you did create an instance of this when you started to test with the BusinessRuleComposer?

Marco

xdg-open; vscode acts as default application for html

Opening html files stopped launching the browser, but launched the vscode application. > xdg-mime query default text/html code-url-h...