Evaluating Business Rules

The Code Effects evaluation engine is an extremely fast XML-based business rules engine that is capable of evaluating millions of source objects against a rule in a matter of milliseconds. There are two ways to evaluate business rules with the Code Effects engine, and three classes that you can use to do so.

You can evaluate your rules in any .NET code (4.0 and up) by using either one of the Evaluator classes or one of Code Effects' extension methods. No ASP.NET or MVC rule editors are necessary during the rule evaluation.

Again, there are two methods that you can use to evaluate rules:

  • Instance. This method is great if you have a collection of source objects of the same type and need to know the result of the evaluation of each member in the collection against the same rule. Or, use it if you simply need to filter out invalid objects from the collection based on a single rule. Use any of the evaluation classes (outlined below) to evaluate your source(s) in this way. For example, you can create a new instance of Evaluator<TSource> class and call its Evaluate method for each source object:

    Evaluator<SourceObjectType> evaluator =
    	new Evaluator<SourceObjectType>(ruleXmlString);
     
    List<SourceObjectType> list = GetSourceObjects();
     
    foreach(SourceObjectType source in list)
    	bool success = evaluator.Evaluate(source);

    The constructor of the Evaluator<T> class used here accepts Rule XML, which can contain a single rule or a large ruleset. It's a good practice to combine a number of rules into one ruleset and pass it to the constructor of the Evaluator class. That way the Evaluator compiles all rules before it starts the iteration through all of the source objects, which saves time. The Evaluate method takes an instance of a source object and returns a System.Boolean that indicates if the evaluation of the instance against the rule was successful. For execution type rules, this Boolean indicates if any of the rule actions were invoked during the rule evaluation.

    Filtering those source objects against the same rule makes sense if you don't care about the result of each evaluation, and just need to filter out those objects that didn't pass through the rule.

  • Extension. This method is great when you have only one instance of a source and need to quickly evaluate it against a rule, or if you have a collection of sources but one or more of them needs to go through a different rule:

    bool success = sourceInstance.Evaluate(ruleXmlString);

    The RuleExtensions class declares several overloads of the Evaluate extension method. Read its topic for details.

Both of these methods use the same speedy Code Effects evaluation engine; they just supply all necessary parameters differently.

Besides extension methods, Code Effects component provides three evaluation classes, each serving a slightly different purpose:

  • CodeEffects.Rule.Core.Evaluator<TSource>. Use this class when you know the type of your source object at design-time. It's the fastest among all evaluation classes (the difference between "the fastest" and "the regular" class for a single source object measures in a few nanoseconds). It's simple to use, and if we have a business rule XML document retrieved from storage as a string, we can simply initiate this class and call its Evaluate method:

    // Retrieve Rule XML from your storage
    string ruleXml = MyRuleStorage.GetRule();
    
    // Init the Evaluator class, passing it the Rule XML
    Evaluator<MySourceObject> evaluator = new Evaluator<MySourceObject>(ruleXml);
    
    // Evaluate instance of your source object against the rule
    bool success = evaluator.Evaluate(sourceObjectInstance);

    You can see that this code is similar to the example at the beginning of this topic except that here we just evaluate a single source object instead of looping through a collection. This class can also be used to evaluate a base class and its descendant types against the same rule. Read this topic for details.

  • CodeEffects.Rule.Core.Evaluator. This is a plain class that is useful in situations where you generate assemblies and/or types dynamically (Emit, CodeDom, Expressions, etc.), and might not know the objects’ types until run-time. This class accepts the source objects' type as a parameter in its constructor:

    Evaluator evaluator = new Evaluator(typeof(MySourceObject), ruleXml);
    bool success = evaluator.Evaluate(sourceObjectInstance);

    With this class, you get more freedom with the small trade-off of slightly slower performance. As with the generic Evaluator class, this class can be used to evaluate the base-child source combinations, too.

  • CodeEffects.Rule.Core.DynamicEvaluator. If you cannot deal with types at all, Code Effects 3.0 introduced a completely new evaluator class - the DynamicEvaluator. It works by creating typed evaluators internally for each new type encountered during rule evaluation. Beware however: since compilation occurs during evaluation, if an object does not declare or inherit members referenced in the rule, it will throw a compilation exception.

    DynamicEvaluator evaluator = new DynamicEvaluator(ruleXml);
    bool success = evaluator.Evaluate(sourceObjectInstance);

    As you can see, the dynamic evaluator has no knowledge of the type of the source right until the rule evaluation.

The last two evaluation classes don't know the exact type of the source object at design-time. But you still need to tell the Rule Editor which type to reflect in order to extract all valid fields, actions, etc., and display them on the client. Without this data the editor simply cannot work. In this situation, do the following:

  • Choose the base class that contains all fields, actions, and in-rule methods that you want the Rule Editor to use on the client, and register that base class as the editor's source object. That gives you your UI:

    <%@ Register assembly="CodeEffects.Rule"
    	namespace="CodeEffects.Rule.Asp" tagprefix="cc1" %>
    
    ...
    <cc1:RuleEditor ID="RuleEditor1" runat="server" SourceAssembly="Test.Project"
    	SourceType="Test.Project.BaseClass" />
    ...
  • Decorate that base class with SourceAttribute, and set its PersistTypeNameInRuleXml property to False (by default it's True). This ensures that there is no type reference in the resulting Rule XML when your rule authors save their rules.

    [CodeEffects.Rule.Attributes.Source(PersistTypeNameInRuleXml = false)]
    public class BaseClass
    {
    	// ...
    }

    You can also set the value of SourceAttribute.DeclaredMembersOnly to False in order to make Code Effects include in the UI all valid fields, actions, and in-rule methods of all base classes that the current base inherits from (if any).

  • Save your rules as usual, and use either the Evaluator or DynamicEvaluator class to evaluate any object that inherits from your base class against those rules without worrying about types.

Because the business needs of organizations and industries can have unforeseeable differences, Code Effects component attempts to be as forgiving as possible, allowing developers to use many kinds of scenarios, and throwing exceptions only when the result of rule evaluation is clearly unpredictable. For example, it allows comparisons of different .NET numeric types without asking developers for explicit boxing or type conversion. It also provides internal type conversion between nullable and regular types when preparing the source for evaluation.

It even allows evaluation of classes that hide base class properties by declaring properties of the same name, even of different types. To illustrate this, let's declare a base class and a child class, each having a property called ID. (Note that the child class changes the type of its ID property from System.Int32 to System.String):

using System;
using CodeEffects.Rule.Core;
 
namespace TestProject
{
	public class MyBase
	{
		public int ID { getset; }
		public string Name { getset; }
 
		public MyBase() { }
	}
 
	public class MySource : MyBase
	{
		// Hiding the base ID property
		new public string ID { getset; }
		public DateTime Date { getset; }
 
		public MySource() { }
	}
}

Now, let's register Code Effects component on some web page, giving it our base class as the source object type, and create a tiny evaluation type rule:

Check if ID equals [2]

And finally, let's evaluate this rule against instances of our base and child classes:

// Base instance
MyBase baseSource = new MyBase { ID = 1, Name = "Base Test" };
 
// Child instance
MySource childSource = new MySource { ID = "2", Name = "Child Test" };
 
// Retrieve Rule XML from your storage
string ruleXml = this.RuleEditor1.GetRuleXml();
 
DynamicEvaluator evaluator = new DynamicEvaluator(ruleXml);
 
// Evaluate the base
bool success = evaluator.Evaluate(baseSource); // Returns False
 
// Now evaluate the child with the same rule
success = evaluator.Evaluate(childSource); // Returns True

The Code Effects engine is smart enough to allow a string with a value of "2" in the rule that uses a numeric value. However, instantiate the child with an ID of "test string" and Code Effects component will throw an evaluation exception.

Post your support requests on Stackoverflow.com. You can also post your comments and product feedback using the form at the bottom of this page.
Comments: 0
Name (optional):
Comment (URLs are allowed and must start with http:// or https://; all tags will be encoded):
Remaining character count: