Writing an expression for a rule is very simple. There are some standard expressionss that can be used. When it comes to extending the expressions and evaluation engine, you have two different options:

  • Passing a reference to an object and calling (invoking) its method
  • Extend the expression by functions

In this post, we are going to describe the second option.

Functions Implementation

If you have another look at the expression standard on our wiki site, you will see we do not have ‘In’ nor ‘Array’ operators:

  • In: Checks whether an element exists in an array
  • Array: Creates or groups a series of values as an array of values

Let’s first create a simple implementation in our application and then I will show you how to use this as part of your expression evaluation.

public static class InExtensions
{
    /// <summary>
    /// Finds value in array
    /// </summary>
    /// <returns>true if value found, otherwise false</returns>
    [Function("in")]
    public static bool In(object value, object[] array)
    {
        if (array == null)
            return false;
        return array.Contains(value);
    }

    /// <summary>
    /// Groups a series of object to one reference
    /// </summary>
    /// <returns>Returns the object that holds array</returns>
    [Function("array")]
    public static object Array(params object[] array)
    {
        return array;
    }
}

As you can see we have decorated out static methods with an attribute named “Function”, which marks the methods with a function name.

Registering Functions and Using These In Expressions

The only thing left to do is to register the type with static methods as functions. The evaluation engine automatically maps the methods marked by FunctionAttribute to a function with the provided function name.

Now let’s see how you can write a simple expression:

[TestMethod]
public void test_extending_expressions_by_functions()
{
    var variable = new VariableContainer();
    variable.RegisterFunction(typeof(InExtensions));

    variable.RegisterVariable("age", 40);
    object result = ExpressionEval.Default.Compute(variable, "in(age, array(3,4,56,60,3,10))");
    Assert.IsFalse((bool)result);

    result = ExpressionEval.Default.Compute(variable, "!in(age, array(3,4,56,60,3,10))");
    Assert.IsTrue((bool)result);

    result = ExpressionEval.Default.Compute(variable, "in((age-30), array(3,4,56,60,3,10))");
    Assert.IsTrue((bool)result);
}

Or if you like, instead of the yellow like and separate class implementation above, you can extend the rules with delegates/functions like this:

variable.RegisterFunction("in",
    new Func<object, object[], bool>((a, b) =>
    {
        if (b == null)
            return true;
        return b.Contains(a);
    })
);
variable.RegisterFunction("array",
    new Func<object[], object>(a => a)
    );

The benefit of using a class implementation is that you can keep reusing them just by adding a Using command into your rules.

Using the New Functions in Decision Tables

Now using new functions in a Decision Table is very simple. You just need to use the ‘Import Function’ type in your decision table:
FlexRule_In_DecisionTable4

Conclusions


Extending business rules expressions by functions is simple as calling one method on your VariableContainer. This makes function a first-class citizen in extending rules and logic in your applications.