en en en en en en en en en en en en en en en en en en en en en en en en en als en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en als en en en en en en en en en en en en en en en pt en en en en en en en en sv fr fr en en en en en en en en en en en en en en en en als en en
FlexRule enables developers to extend its functionality in many different ways for adding new or modifying existing runtime behaviors.
Extending functionality at the expression evaluation level can happen in two ways:
You can define functions in two ways:
To register by static methods:
Create a class (e.g., InExtensions)
Create static methods and mark them by Function
For example, there is a class to define two functions:
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 have a simple implementation in our application and then demonstrate how to use this as part of your expression evaluation.
class CollectionOps { ///   /// Finds value in array ///   ///  true if value found, otherwise false  [Function("in")] public static bool In(object value, object[] array) { if (array == null) return false; return array.Contains(value); } ///   /// Groups a series of objects to one reference ///   ///  Returns the object that holds the array  [Function("array")] public static object Array(params object[] array) { return array; } }
As you can see, we have decorated the static methods with an attribute named Function which marks the methods with the function name.
There are other ways to register a function as well. Not all of the types you want to register to allow you to add Attributes on top of them. So in those cases, one option is to use anonymous delegates.
variable.RegisterFunction("in", new Func ((a, b) => { if (b == null) return true; return b.Contains(a); }) ); variable.RegisterFunction("array", new Func (a => a) );
Use any existing static method of any type as a function by its method signature. The signature of a method is:
{namespace}.{type name}.{method name}({its arguments})
For example, a System.Threading.Thread is a type in .Net Framework and it has a method called Sleep.
The signature of the method would be:
And you can register the method as a function.
The defined function will be called during the evaluation of the expression. In order to get the context of the evaluation in some advanced scenarios, you can add an extra argument to your method with the type of IDynamicEvaluationContext. During the evaluation, the context will be passed to your method.
[Function("insert", Pipe = true)] public object Insert(IDynamicEvaluationContext context, object value, object index, object obj) { // context will be passed in to your method. }
In the above implementation, we are introducing the monadic operator (function) called an insert.
IDynamicEvaluationContext must be the first argument of your method.
The only thing left to do now 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.
The registration can be done via:
Import Function (in Excel only)
To register a static method as a function, simply put the method signature in the path of Using command.
Note that in the above example, GetDays now is a function that can be used directly in your logic.
Code using RegisterFunction on VariableContainer or RuntimeEngine instance.
var variable = new VariableContainer(); variable.RegisterFunction(typeof(InExtensions));
var engine = RuntimeEngine.FromXml(InMemoryFileStore.GetByName("DiscountDecision.xml").AsBytes()); engine.RegisterFunction(typeof(AgeExtensions));
The registration function can also be part of the document definition’s declaration section:
Note that in the above example, all of the Functions on type RuleUtilities will be registered as part of the execution context.
Excel for Decision Tables
As mentioned previously, before using a function you need to register it in the logic document. In an Excel-based Decision Table use, the ‘Import Function’ with the ‘type@assembly’ format and the rest will be taken care of automatically.
Once the functions are registered, you can call them like any other functions. These will be available in the context you have registered (i.e., the logic document). You can use those anywhere in any logic, or use them directly as part of evaluating an expression. Use this technique to simply extend the expression and call a custom code via your logic as a function. The benefit of the function is that it will be available globally, which means to execute it you don’t need to use an object instance or type name.
As an example, here is a test that evaluates the two new functions we introduced as part of direct expression evaluation.
[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); }
It is also possible to extend the expression by other logic that is already modeled. For example, a use case would be in a Natural Language for which there is a need to connect to the database in order to retrieve data.
In order to do that:
Model your logic (i.e., data retrieval in this example) in some other type (i.e., a procedural logic)
Register your logic as a function in using as part of the Declaration section of your rule (i.e., the Natural Language)
Setting up in Designer
Open the “Type Definition” that is going to use the function in your current model:
Then add the following settings by adding a new item:
And in your actual model (i.e., Natural Language) you can do the following:
Please note, that you can accomplish the same thing for any type of logic (e.g., Flow, Decision Table, Tree, etc.) to use any other type of logic as a function.
And in your LoadDataFromDb.xml you have something similar to the model shown below:
Monads are a specific type of function that can be chained together using the pipe operator. You can simply create your own monads by defining a custom class like as you would for a function using FunctionAttribute the only difference is to set the attribute Piple=true.
static class CustomPipe { [Function("increase", Pipe = true)] public static object IncreaseNumber(IDynamicEvaluationContext context, object value) { if (value == null) throw new ArgumentNullException("value"); var number = (int)value; return number + 1; } }
Please note, your monad function signature must have at least the two arguments as shown in the above example:
context: the context of expression evaluation
value: the left operand of the monad that is passed in for processing
If the monad requires more arguments, you can add them as object parameters.
And its usage will be similar to the code below:
[TestMethod] public void test_custom_pipe() { var vc = new VariableContainer(); vc.RegisterFunction(typeof(CustomPipe)); var result = ExpressionEval.Default.Compute(vc, "2|increase()|increase()"); Assert.AreEqual(4, result); }
In the .Net framework, extension methods are compiled time-binding. In FlexRule Runtime there is no compilation and models are running at run-time. Therefore, to use extension methods you need to register them as functions. In so doing there are a couple of options:
Create a utility class that wraps your extension method and applies FunctionAttribute to its methods.