While debugging a pipeline component that made use of a Business Rules Engine (BRE) policy I noticed some behavior that stopped me right in my tracks (funnily enough, I actually enjoy being surprised). Said component was asserting a .NET object into the BRE policy which contained multiple rules, some calling on the same methods exposed by the object with exactly the same parameters. My assumption has always been that the BRE would cache all the details about facts that are used in the rules, and when forming up it’s RETE algorithm it would reuse cached details rather than fetch them from the actual facts where possible. This assumption turned out to incorrect.
Rather than badgering on about this, let’s illustrate this with a quick example. I have a helper class which contains a string property called Name, an int called Counter, and a boolean called Match. The get accessor for the Name property will increment the counter by one to make note that the get accessor has been called. The class has a constructor which sets the Name property. The project has been signed and the dll has been registered in the GAC to ensure that the BRE can work with this class.
Next I whipped up a very quick vocabulary with a definition called GetName and another called SetMatch. Once the vocabulary was published I constructed a policy which contain multiple rules, each containing a condition that evaluates the GetName definition against constant values. If the conditions for any of the rules are evaluated then the rule will use the SetMatch definition to set the Match boolean to true.
Next up I whipped up a console applicaiton which instantiates a helper object with a name of Jes and then asserts the helper object into the aforementioned policy. After the policy executes it will write the Match boolean as well as the value of Counter (which will tell us how many times Name’s get accessor has been called) to the console. Below is the code to call the policy.
When executing the policy one would expect Counter’s value to be 1 since GetName should result in the same value no matter how many times it is used as part of a condition. However the output in the console looks like the below (note that there were four rules in the policy).
After doing some reading, it turns out that this behavior is specific to .NET based facts and can be overridden, however not too easily, and with some implications (which I imagine are quite unlikely in the majority of scenarios but are altogether possible). The means to overriding this behaiour is through a setting called SideEffect which sits on a function level in a BRE policy. This setting can’t be adjusted in the Business Rules Composer however. The simplest way to update this setting is by exporting a policy to an XML file via the Business Rules Engine Deployment Wizard, updating the XML file, and then importing it back in (either remove the existing policy before importing or increment the version number in the file before importing it to ensure there is no clash). Now you might be thinking that if you have to update this setting in such a roundabout way then surely Microsoft never intended for this setting to be changed and the implications are probably too dangerous, however Microsoft themselves suggest that this setting should be tweaked to achieve maximum performance for BRE policy execution (relevant section in the below screenshot) – http://msdn.microsoft.com/en-us/library/ee377076(v=bts.10).aspx.
As far as implications go, I would suggest having a read of http://geekswithblogs.net/cyoung/archive/2007/04/09/111169.aspx. This is an absolutly fantastic post that goes into great depth detailing how the BRE works, and discusses the implications of the SideEffects property in details. From what I gather, SideEffects is defaulted to true for .NET objects as it is always possible, especially in a multi-threaded application scenario, that the object might actually be manipulated in a seperate thread between condition evaluations and thus the results might be different each time a condition is evaluated. Chances are that this is not going to be likely for the majority of use cases involving the BRE but I suggest understanding the implication for your specific case if you decide to manipulate the SideEffects property.
The property that needs to be updated in the policy extract XML file is as below (note that you will need to update the property for all the rules in your policy that you want to be able to take advantage of caching).
After updating the extract file and then deploying the updated policy to the rules engine, the console application now outputs the expected value for Counter.
In summary, if you are making use of BRE policies that make use of .NET class based facts (and I encourage you to do so when possible as there are performance benefits when using .NET class based facts rather than XML based facts), you should ask yourself whether your policy would benefit through the use of caching of fact details or not. While it is possible that the gains might be quite minimal, it could make all the difference in a high throughput or low latency scenario and thus this property should not be overlooked.
If you are interested in playing with this, I have uploaded a copy of my sample solution to my Google Drive folder. Remember that you will need to GAC the Helpers project and you will also need to import the Vocabulary and Policies (contained in the BRE Artifacts sub-folder) and deploy the policies (only deploy the relevant one that you want to test) before the console application will work.