◷ Reading Time: 9 minutes
Introduction
In a procedural rule, you can instantiate an object and access its member. You may access the object member if you want to:
- Read or set a property value
- Invoke a method on the object
Defining and instantiating
The first step in creating an object is to define a parameter that can be used to access the newly instantiated object. To define a parameter, we simply use the Declaration section and use the Define command.
For example, in the following sample rule we have defined three parameters:
<Procedure name="Sample1" enabled="true">
<Declaration>
<Define name="time" direction="out" />
<Define name="item" direction="out" />
<Define name="discount" direction="out" />
<Define name="hasPassedvalidation" direction="out" />
<Define name="firstName" direction="out" />
<Define name="firstNameLength" direction="out" />
</Declaration>
</Procedure>
Please note that the defined parameters do not need to specify types.
Primitive types
Primitive types do not need instantiation. For example, a string can be assigned to the parameters just by using Var command. We will describe this later.
Complex and Custom types
In a complex type, the rule must create an instance (if not created yet) and then reference the parameter to access the members. To create an instance, you can use
Using New Command
Let’s say we want to create a DateTime and assign it to our defined parameter named time.
<New name="time" type="System.DateTime">
<Constructor />
</New>
Of with no Constructor when no parameter is required.
<New name="time" type="System.DateTime"/>
Note that the Constructor calls the default constructor on the type to instantiate it. However, there might be some types in which you may need to use other overloads of constructors. To do that you need to pass some input values to the constructor arguments. In such cases, you can use the Param command.
<New name="start">
<Constructor>
<Param value="10i"/>
<Param value="30i"/>
<Param value="0i"/>
</Constructor>
</New>
Custom types
In some scenarios, you need to create a specific type (your application`s types) that is located in your assembly. Then you have to specify the type information in the New command.
<New name="item"
assembly="SampleApplication.Library.dll" type="Entities.Person">
<Constructor>
<Param value='"Arash"'/>
</Constructor>
</New>
Or if you have set up the typeId information in the execution context, you can only specify the typeId.
<New name="item"
typeId="Person">
<Constructor>
<Param value='"Arash"'/>
</Constructor>
</New>
To setup the typeId you have to use the following code to register type identifiers before executing the procedural engine.
byte[] procedure=... // loading the rule content here.
var engine = RuntimeEngine.FromXml(procedure);
engine.OnRunning = (e)=> e.ExecutorSetup.TypeIdRegistry.Register("Person", typeof(Officer));
Using expressions
At the expression level, new and newOf are functions that create new instances of a type. These functions are part of the standard expression languages and are explained in the Expressions. You can evaluate the expressions in Procedural and Validation logic using Var commands.
<Procedure name="new date time sample>
<Declaration>
<Define name="obj" direction="out" />
<Using path="System.DateTime" />
</Declaration>
<Var value="obj = new(DateTime, 1977, 3, 1)"/>
</Procedure>
Read and write properties
When an object exists: either by instantiating using the methods we described here or by being passed by application, then in your logic you can access its properties to read or set new values. You can use Var commands to access its properties, set a values parameter or set a value to an object`s property. All Var commands must have a name setting. The name would refer to the name of a defined parameter in the rule based on the current Scope. If the scope is not defined, it refers to the root declaration section of the rule.
To start with a simple example, let’s try to set a string value like “Arash” to our defined variable named name.
<Var name="firstName" value='"Arash"' />
Now let’s try to set the firstNameLength to be set to the length of firstName.
<Var name="firstNameLength" value="firstName.Length" />
Then let’s see what we have modelled as the rule so far:
<Procedure name="Sample1" enabled="true">
<Declaration>
<Define name="firstName" direction="out" />
<Define name="firstNameLength" direction="out" />
</Declaration>
<Var name="firstName" value='"Arash"' />
<Var name="firstNameLength" value="firstName.Length" />
</Procedure>
Let’s assume we have set up the type identifier as Person and we have instantiated it. This Person type has a property named FirstName and we want to set the property of the instantiated object. To access the internal properties of an object to be set, you can use pathsetting of the Var command.
<Var name="person" path="FirstName" value="firstName" />
Or the other alternative for this is using assignment operator
<Var value="person.FirstName = firstName" />
Let’s put everything together now:
<Procedure name="Sample1" enabled="true">
<Declaration>
<Define name="firstName" direction="out" />
<Define name="firstNameLength" direction="out" />
</Declaration>
<Var name="firstName" value='"Arash"' />
<Var name="firstNameLength" value="firstName.Length" />
<New name="person">
<Constructor />
</New>
<Var name="person" value="person.FirstName = firstName" />
</Procedure>
Invoke methods
To invoke a method on an instantiated object you can use the following:
- CallMethod command
- Var command and method invoke expression
CallMethod
This command not only allows you to invoke a method of an object, but it also lets you store the returned value of the execution to a defined parameter in your rule.
Let’s say that in our example, the Person type has a method call GetHistory that lists all the driving fines of the person. The rule then simply checks if a person has any fines in the last two months.
<CallMethod method="person.GetHistory" return="fineList">
<!-- last two months fine retrieval-->
<Param value="2i"/>
</CallMethod>
Please note that we assume you already defined fineList as a parameter in your logic’s declaration section.
Using expressions
Method call invoke action is supported also in expression level. The Var command evaluates an expression, so when the expression is calling a method, the evaluation results invoking the method.
<Var value="fineList = person.GetHistory(2i)" />
Example
The logic for this example is to discover whether a person has had any fines recently, and allows the application to retrieve the list of fines from the execution context. We do not ask the application to define the meaning of recently by passing the number of months to be retrieved. It is up to the rule abstraction to define what recently means.
Now let’s put everything together:
<Procedure name="IsRecentlyFined" enabled="true">
<Declaration>
<Define name="personId" direction="in" />
<!-- application can read the following two values from context -->
<Define name="fineList" direction="out" />
<Define name="hasAnyFines" direction="out" />
</Declaration>
<Var value="person = new (Person, personId)" />
<Var value="fineList = person.GetHistory(2i)" />
<Var value="hasAnyFines = fineList.Count gt 0"/>
</Procedure>
Custom code
There are multiple ways of using custom code as part of your logic execution:
- Static Members: You can register your type and assembly using Using command in any logic (i.e., Flow, Procedural, Decision Table, etc.) and start using the members of the type.
- Custom Function: Define your methods in your custom code as a function and use them as FlexRule functions.
Static Members
To access static members (Properties and Methods) of a Type, the type must be registered with the Using command. Then it can be used with a registered name.
<Procedure name="Now" enabled="true">
<Declaration>
<Define name="now" direction="out" />
<Using path="System.DateTime" />
</Declaration>
<Var value="now = DateTime.Now" />
</Procedure>
Custom Functions
This allows you to extend FlexRule’s functions list and use your custom code as a normal function as part of the execution.