From FlexRule Wiki
Jump to: navigation, search


A logic may have some parameters. Parameters are a gateway with which to communicate with the logic. These enable both application-to-logic and logic-to-logic communication. This communication enables your application to pass information of any type to the logic or to read some information from the logic.

There is no limit to the number of parameters and types of value that you can pass to and retrieve back from the logic. A parameter can be both an internal and custom type (i.e., any CLR types can be used, as well as dynamic and anonymous types). These types can be defined in different assemblies and be referenced in rules.

In FlexRule there are two types of parameters:

  1. Variable parameters (place holders for values)
  2. Type parameters


Input parameters allow your application to send some input and let the logic process it.


After processing is finished, then your application can collect the result from output parameters as well. These parameters can define the decision model of logic to be collected when the process is finished.


Sometimes one parameter must act as both the Input and Output, in which case this direction type can be used.


In some scenarios, processing requires some private, local parameters to store the result into those slots temporarily and then collect them some other time. The parameters that are not part of your logic decision are called local parameters.

How to define

All logic (e.g., Flow, Procedural, Workflow, Validation, etc.) can define parameters by adding a Declaration. In this section, your logic is able to define parameters by using Define.

By setting the direction of the Define command, you can specify what type of parameter is being defined. While the type setting of the parameter defines the type of parameter, it is not necessary to define this in most cases.

No types

Using literals can define the value type when this is being assigned.

  1. <Declaration>
  2.   <Define name="StarPrice" direction="out" value="100i" />
  3.   <Define name="StarQty" direction="in" value="0i" />
  4.   <Define name="StarQtyDiscount" direction="in" value="0d" />
  5.   <Define name="result" direction="out" />
  6. </Declaration>

Primitive types

  1. <Declaration>
  2.   <Define name="StarPrice" type="System.Int32" direction="out" value="100"/>
  3.   <Define name="StarQty" type="System.Int32" direction="in"/>
  4.   <Define name="StarQtyDiscount" type="System.Double" direction="in"/>
  5. </Declaration>

Complex types

When your logic requires a complex type, you can use assembly and type to define the parameter.

  1. <Declaration>
  2.   <Using assembly="InvoiceData.dll" path="InvoiceSample.Data" />
  3.   <Define name="dataInfo" assembly="InvoiceData.dll" type="InvoiceSample.Data" direction="Local" />
  4. </Declaration>


There are three directions supported within the framework. Directions are for encapsulating the parameters are being defined. It can be set by the direction setting of the Define command.

Bear in mind that when your logic has defined some input parameters, they have to be passed to the logic during execution, otherwise when the logic gets executed, you will receive an exception regarding the missing values for those defined input parameters.

Passing Values to Input Parameters

All engines provide a Run method to be used to execute the logic. Those Run methods have two different overrides in the API:

  1. public object Run(IDictionary<string, object> inputParameters);
  2. public object Run(params object[] inputParameters);

The Run method accepts the values and references your logic Input Parameter. You can pass the input as an array of an object or by the name of input parameters in the logic.

This example shows you how to pass input parameters to your logic:

  1. public void RunEngine(int mixed, int gold, int silver)
  2. {
  3.     // This method creates and returns an engine (e.g., ProcedureEngine, FlowEngine, etc.)
  4.     var engine = GetEngine();
  5.     // And we can simply pass the input parameters to the engine
  6.     // by passing all of them to the run method of the engine
  7.     engine.Run(mixed, gold, silver);
  8. }

Passing an Array into rules

Because engines accept input parameters as params object[], when your input parameters themselves are in an array then you have to wrap them in another array of an object.

Let's consider logic that has the following signature:

  1. <Declaration>
  2.   <Define name="intArg" direction="in" />
  3.   <!-- array of string -->
  4.   <Define name="arrayArg" direction="in" />
  5.   <Define name="dateArg" direction="in" />
  6. </Declaration>

In your application, when you are going to call Run or Validate methods to execute the logic, you need to pass the input parameters.

In this scenario, you need to pass the following object to the execution method (e.g., Run, Validate, etc.):

  1. var inputs = new object[]
  2.     {
  3.         10, 
  4.         new object[] {"test1", "test2"},
  5.         DateTime.Now
  6.     };

Please note that in the modeling stage, there is no difference between a parameter as an array or as a single value. The values that are passed in will determine their type of parameter.

Reading Values from Output Parameters

When the execution of the engine is finished, all of the output parameters of the logic can be retrieved from the engine context. Also, during the read you can make sure the process has successfully finished by checking the 'Exception' property of the context.

  1. public void ReadValues(ActiveElementEngine engine)
  2. {
  3.     // reading the paramters from context
  4.     var engineContext = engine.Context;
  5.     if (engineContext.Exception != null)
  6.         throw new Exception("Something went wrong.", engineContext.Exception);
  8.     // Getting the execution context variable container
  9.     var variables = engineContext.VariableContainer;
  11.     // reading the values from a variable container
  12.     // In this line we read a value of an output parameter named 'InvoiceTotalAmount' from the logic
  13.     decimal total = (decimal) variables["InvoiceTotalAmount"];
  15. }

Variable Container

All of these parameters can be accessed in a container called Variable Container which exists in your execution context. Once an engine is created, all of the Variable and Type parameters will be registered in the variable container. In code also, you can create a variable container manually and evaluate an expression based on the created container.

  1. public void test_select_contains()
  2. {
  3.    var list = new List<DecisionAgeTests.Person>()
  4.    {
  5.        new DecisionAgeTests.Person("arash", 38, DecisionAgeTests.Gender.Male),
  6.        new DecisionAgeTests.Person("Parsa", 6, DecisionAgeTests.Gender.Male),
  7.        new DecisionAgeTests.Person("arash", 38, DecisionAgeTests.Gender.Male),
  8.        new DecisionAgeTests.Person("Shah", 3, DecisionAgeTests.Gender.Female),
  9.        new DecisionAgeTests.Person("Shah", 31, DecisionAgeTests.Gender.Male)
  10.    };
  12.     var vc = new VariableContainer();
  13.     vc.RegisterVariable("people", list);
  14.     vc.RegisterVariable("$name");
  15.     vc["$name"]="arash";
  17.     var res = ExpressionEval.Default.Compute(vc, "people |groupBy('p', 'p.Name') |where ('g','g.Count>1') |select('d','d.Key') |contains($name)");
  18.     Assert.AreEqual(true, res);
  19. }

As you can see in this example, a variable container is used to hold the values and types that are used for evaluating an expression.

Flexible Typing

Parameters that are defined in a VariableContainer are not referencing any type and can be used just as a placeholders within an expression. This allows you to decouple the entity definition of your application from the model of the business logic (e.g., rules, decision, etc.). As long as the passing object is syntactically correct, the expression can be evaluated. Therefore the logic model can be executed.

  1. class Table
  2. {
  3.     public int Length { get; set; }
  4. }
  6. [TestMethod]
  7. public void Expression_Eval_Flexible_Typing()
  8. {
  9.     const string expression = "p.Length==8";
  10.     IVariableContainer container = new VariableContainer();
  11.     container.RegisterVariable("p");
  13.     // string type has Length property
  14.     container["p"] = "FlexRule";
  15.     Assert.AreEqual(true, ExpressionEval.Default.Compute(container, expression));
  17.     // an anonymous type with Length property
  18.     container["p"] = new { Length = 8 };
  19.     Assert.AreEqual(true, ExpressionEval.Default.Compute(container, expression));
  21.     // Array has Length property
  22.     container["p"] = new[] { 1, 2, 3, 4, 5, 6, 7, 8 };
  23.     Assert.AreEqual(true, ExpressionEval.Default.Compute(container, expression));
  25.     // static type that has Length property
  26.     container["p"] = new Table {Length = 8};
  27.     Assert.AreEqual(true, ExpressionEval.Default.Compute(container, expression));
  29.     // Expando (dynamic) type that has a Length property
  30.     dynamic x = new ExpandoObject();
  31.     x.Length = 8;
  32.     container["p"] = x;
  33.     Assert.AreEqual(true, ExpressionEval.Default.Compute(container, expression));
  34. }

As you can see in this example, in this expression, the p.Length == 8 the p identifier is just a placeholder for whatever has a member (property or field) named Length. As highlighted in the above example, different types of value have been assigned to the parameter p in the variable container.

Now in any logic type, when you define input parameter with no specific type defined, then you can pass any of those values to the logic without restricting your logic with any type.

Types in FlexRule Designer

Please see details at here.

Nested Logic

In a hierarchical logic document such as a Flow, the (parent) may contain some other logic, such as a Decision Table (child). To pass some values from parent to child logic, we need to add a Parameter with the same name in both logic documents (parent and child), and also set the context for the child to be shared.

For example, in the example below we have a Flow (as a parent) in order to find the kid's category based on their age. Our Flow contains a Decision Table (a child) to carry our rules.

Shared Context

Shared Context is used when the relationship between parent and child logic is simple (i.e., There is no conversion is required to pass value of Parent Parameter to Child Parameter).

In this Flow, we have an Input Parameter (which is Age) and an Output Parameter (which is Category). Now if we define both these Parameters in our Decision Table, then their context would be shared. This is because the added Parameters have the same NAME as shown below:

We have created two Parameters (Age and Category) in the Flow, and for purposing the shared context, we have created two Parameters (Age and Category) in our Decision Table.

New Context

New Context is used when the relationship between parent and child logic is not simple. This means passing parameter's value from parent to child requires conversion (e.g., sending an object (JSON) into the Flow which has some attributes that the Decision Table requires). In these cases, we need to set a mapping between the parent's parameter to the child's parameters. In the Flow, this is done by selecting the Decision Table (or other child logic) and:

  1. Setting the execution context to New
  2. Adding the map using the Parameters properties


For example, when we pass a complex structure (e.g., JSON) that has some attributes (e.g., Age) the settings below need to be applied:

  • Add an input Parameter in our Flow and call it "record" (record has the Age as an attribute and we need to pass it to our Decision Table).
  • Add an input Parameter for our Decision Table and call it "Age".
  • Change the Decision Table's Context Mode to New.
  • Add a Parameter for our Decision Table inside the Flow (select the Decision Table in the Flow/click on Properties/Parameters):

The Name is "Age" and its Value is: "record.Age"(because it is using the record's attribute which is "Age"):


  • Now the Flow can be fed by a simple JSON as below:
  1.  {
  2.   "Age": 12 
  3.  }