1. Home
  2. RuleSet

RuleSet

Print Friendly, PDF & Email

◷ Reading Time: 7 minutes

What is a RuleSet

RuleSet is an in-memory container that holds logic models and gives an addressing capability to access an already loaded model. RuleSet allows the categorizing of rules in a nested (hierarchical) manner. This function allows logic and rules to be grouped conceptually based on your problem domain. Although we call this container RuleSet, please note that it is a container that stores all of the rules and logic in-memory. This logic can be business rules, application flow, procedure, validation, decision, decision table, etc., as well as anything that implements IElementModel. All models can be grouped and stored in one or more RuleSets.

Splitting and Grouping

The way you group and organize these models varies by system. Depending on your architecture and the complexity of the scenario and logic, you may have more than one RuleSet for a scenario. Grouping and organizing rules and logic sources into folders is common sense, especially when you have many of these and you need to selectively load them as part of different user stories or scenarios.

Let’s say you are writing a medical/health system and for a particular scenario you need to build a RuleSet like this:

Addressing

Each logic document can be identified with two names:

  1. File name
  2. Logic name

Therefore addressing logic documents inside a ruleset can be done either by using:

  • relative path and filename
  • ruleset:// protocol and logic name

Path and Filename

When a ruleset is created from a RuleFile then the address of the logic documents inside a ruleset can be achieved using a relative path and filename.

Format: folder1\folder2\...\filename.xml

Using Protocol

RuleSet addresses are relative addresses to the root of the RuleSet. They start with the ruleset:// protocol. Include your hierarchy (e.g., sub-folders) and the last part is the logic (i.e., rule) name.

Format: ruleset://sub1/sub2/sub3/.../logic name
logic name is the name attribute of the root element on each document.
  • sub1, sub2, sub3,…: These are categories you can define to organize your rules in a RuleSet. It is very similar to organizing your files in a File System.
  • logic name: The name you defined for the logic. This is not the logic’s file name. It is the actual model name for specific logic.

Example: ruleset://validation/common/DateCheck

Where DateCheck is the name of the validation logic.

No Hierarchy

You may store your logic in a RuleSet directly, without organizing it in a different hierarchy (e.g., sub folders). In this case you will have the following format.

Format: ruleset:///logic name

As you can see, the above format still follows the original, but does not include any category (e.g., sub folder)

How to Build a RuleSet

There are multiple ways to build a ruleset:

  1. Using RuleSetFactory
    1. Create from a folder structure
    2. Create from a Package file (standard zip package)
  2. Manually loading models into a ruleset

From Directory

When building a ruleset using DirectoryInfo, you can create two types of ruleset that each support different addresses based on

  • Relative path and filename
  • Protocol url

Path and Filename

Using RuleSetFactory.FromRuleFiles will create a ruleset in which each logic document can be addressed by a relative path and filename.

var path= @"ADDRESS\TO\RULES\FOLDER";
var rulesLocation = new DirectoryInfo(path);
// we can build the ruleset now
var ruleset = RuleSetFactory.FromRuleFiles(rulesLocation);

Protocol Url

Using RuleSetFactory.FromDirectory creates a ruleset in which each logic document can be identified by a protocol address

var path= @"ADDRESS\TO\RULES\FOLDER";
var rulesLocation = new DirectoryInfo(path);
// we can build the ruleset now
var ruleset = RuleSetFactory.FromDirectory(rulesLocation);

Manual

The manual process of building a ruleset allows you to selectively add different logic documents to the collection.

Path and Filename

Using FromRuleFiles(IEnumerable<RuleFile>) creates a ruleset in which each document can be addressed by relative path and filename.

var rs = RuleSetFactory.FromRuleFiles(new List<RuleSetFactory.RuleFile>()
            {
                new RuleSetFactory.RuleFile("doc1.xml", null, File.ReadAllBytes(Path.Combine(Folders.Data, "Database", "doc1.xml"))),
                new RuleSetFactory.RuleFile("doc2.xml", null, File.ReadAllBytes(Path.Combine(Folders.Data, "Database", "doc2.xml"))),
                new RuleSetFactory.RuleFile("doc3.xml", "folder1", File.ReadAllBytes(Path.Combine(Folders.Data, "Database", "doc3.xml")))
            });

Protocol Url

You can also pick different files from different locations and build your ruleset manually:

  1. Loading your model using LoadAdapterUtility
  2. Adding each model to Hierarchical RuleSet

Use AddModel extension method below:

/// <summary>
/// Allows the addition of individual models as part of a ruleset
/// </summary>
/// <param name="rs">Ruleset to append the model to in a specific location as <paramref name="query"/></param>
/// <param name="query">Address of a model. Can be nested by / (i.e., rules/flows/purchase</param>)
/// <param name="models">List of models to be added into the <paramref name="query"/> section</param>
public static void AddModel(this IRuleSet rs, string query, params IElementModel[] models)

For example:

public IRuleSet LoadFlowRuleSet()
{
    var ruleset = RuleSet.HierarchicalRuleSet();
 
    // adding all activities
    ruleset.AddModel("flow/activities",
        ReadModel("SendEmail.xml"),
        ReadModel("AssignToManager.xml"),
        ReadModel("AssignToUser.xml")
    );
 
    // adding libraries procedures
    ruleset.AddModel("flow/library", ReadModel("CreateAssignment.xml"));
 
    // adding the main flow
    ruleset.AddModel("flow", ReadModel("CaseHandlingWorkflow.xml"));
 
    return ruleset;
}
 
private IElementModel ReadModel(string path)
{
    // _basePath is the base location that all models are located
    var fullPath = Path.Combine(_basePath, path);
    return LoadAdapterUtility.LoadModel(fullPath);
}

Where RuleSet can be used

Reference external logic

1. ProcSource command of CallProc

Summary

In a procedural engine, multiple procedures can be interlinked. This means that a procedural rule can execute another rule; thus, the context of the parent rule can be shared, and the result can be copied back to the root context. Different scenarios can be managed in several ways.

Parameters

In all bellow commands uri can refer to a RuleSet address ruleset:// when the execution plan is loaded by the RuleSet or RuntimeEngine with RuleSet is used:

  1. If your application logic is complex and requires multiple Procedural rules, this command can call an externally defined rule and return the result to the running execution context.
  1. ProcSource command of CallProc
Summary
In a procedural engine, multiple procedures can be interlinked. This means that a procedural rule can execute another rule; thus, the context of the parent rule can be shared, and the result can be copied back to the root context. Different scenarios can be managed in several ways.
Parameters

  1. contextMode
    1. Description: Sets the context for the called procedure
    2. Mandatory: Yes
    3. Types
      1. New: Creates a new context and uses such context for execution; in this case, the variable of the new context must be registered by the Param command
      2. Shared: Uses the current context for execution and the new procedure after finishing the procedure execution; the current execution variable context (of only out-variables) is copied to the original one
  2. resultCopyMode
    1. Description: This attribute is set when the target procedure has variables that need to be accessed via the root executing procedure.
    2. Mandatory: Yes
    3. Types
      1. None: Does not copy
      2. AddOut: Copies out-variables
      3. AddIn: Copies in-variables
      4. AddBoth: Copies both in- and out-variables
  3. return
    1. Description: Sets a value of a parameter as a return value of the operation/call
    2. Mandatory: No
    3. Types: String (name of a parameter

Internal Commands

Param

This command passes a value to the procedure and registers new variables in a new context if required.

ProcSource

This command provides the rule with a source to be loaded and executed. It can be a relative or absolute file path or a RuleSet address. If it is a relative file path, the location is initiated from your application’s execution folder. If it is a RuleSet address, it should be a valid address, and the engine should be initialized using RuleSet overload.

Sample

<CallProc contextMode="Shared" resultCopyMode="AddOut" >
   <ProcSource uri="Rules/Ver2/CalculatePriceSubTotal.xml" />
   <!-- 
         When we use contextMode="New" we need to pass the
         required parameter for the target procedure, otherwise
         all of the variables are shared in the main procedure and do
         not need to pass <Param .... />
 
   <Param name="StarQty" ref="StarQty"/>
   <Param name="GoldQty" ref="GoldQty"/>
   <Param name="SilverQty" ref="SilverQty"/>
 
  -->
   <Param name="StarQtyDiscount" ref="StarQtyDiscount"/>
   <Param name="GoldQtyDiscount" ref="GoldQtyDiscount"/>
   <Param name="SilverQtyDiscount" ref="SilverQtyDiscount"/>
 
</CallProc>

2. FlowSource command of CallFlow

Summary

CallFlow allows calling a flow with another logic.

Parameters

  1. contextMode
    1. Description: How the parameters are shared with the calling Flow
    2. Mandatory: No
    3. Type: New, Shared

Internal Commands

FlowSource

Parameters

  1. uri
    1. Description: Where is the source of flow to be run
    2. Mandatory: Yes
    3. Type: Physical file or RuleSet address

Sample

<Flow name="Mainflow" version="1.0.0.0">
  <Declaration>
    <Define name="a" direction="in"/>
  </Declaration>
  <Nodes>
    <Start name="Start1">
      <Transition name="Transition8" to="Activity1" />
    </Start>
    <End name="End1" />
    <Activity name="Activity1">
      <CallFlow contextMode="Shared">
        <FlowSource uri="Subflow.xml" />
      </CallFlow>
      <Transition name="Transition3" to="End1" />
    </Activity>
  </Nodes>
</Flow>

3. DecisionTableSource command of CallDecisionTable

Parameters

  1. contextMode
    1. Description: Set context behaviour to call to other rules and logic. If set to Shared, the current context will be used for the target rule. Otherwise a new one is created.
    2. Mandatory: Yes
    3. Type: String (Shared/New)

Internal Commands

DecisionTableSource

  1. uri
    1. Description: Where is the source of the external DecisionTable to be run?
    2. Mandatory: Yes
    3. Type: Physical file or RuleSet address

Sample

DecisionTable integrated into a Flow.

<Activity name="ActivityD">
  <CallDecisionTable contextMode="New">
    <DecisionTableSource uri="AgeTitle.xml" />
    <Param name="person" ref="person"/>
  </CallDecisionTable>
  <Transition name="tr6" to="end"/>
</Activity>

DecisionTable integrated into a Procedure.

<CallDecisionTable contextMode="Shared">
  <DecisionTableSource uri="AgeTitle.xml"/>
</CallDecisionTable>

4. DrdSource command of CallDrd

5. GlossarySource command of GlossarySource

Parameters

  1. uri
    1. Description: Sets address to a glossary, it can be relative file system address or a ruleset address
    2. Mandatory: Yes (when it is the container for defining Terms)
    3. Type: String

Sample

Note in the Condition section (lone 10 to 15) no expression is used and instead term is used.

<DecisionTable name="Define potential occupant injery rating" processAll="False">
  <Declaration>
    <Define name="car" direction="In" />
    <Using path="FlexRule.Samples.CarInsurance.AirbagType" assembly="Car.Library.dll" />
  </Declaration>
  <Glossary>
    <GlossarySource uri="Glossaries\Rating-BusinessGlossary.xml" />
  </Glossary>
  <Columns>
    <Condition name="Driver's Airbag" term="Has Driver's Airbag" />
    <Condition name="Front Passenger's Airbag" term="Has Front Passenger's Airbag" />
    <Condition name="Side Panel Airbag" term="Has Side Panel Airbag" />
    <Condition name="Roll bar" term="Has Roll bar" />
    <Condition name="Convertible" term="Is Convertible" />
    <Action name="Occupant Injury Rating" term="Set Occupant Injury Rating" />
    <Name name="Rule" />
  </Columns>
  <Data>
    <Row>
      <Value>true</Value>
      <Value>false</Value>
      <Value>false</Value>
      <Value></Value>
      <Value></Value>
      <Value>High</Value>
      <Value>R1</Value>
    </Row>
  </Data>
</DecisionTable>

6. ValidatorSource command of Validator

Also the RuleSet address can be used in the Include command

  1. source setting of Include
Summary
This command enables you to split your procedural rules into more dynamic scopes and to import each scope to the body of the rule during the execution process. By supplying the source to be imported as a string or IElementModel type, the scope will be considered as part of your rule body.

If the imported scope is a string type, it must be encoded as a valid XML or RuleSet address. If it is an XML, its special characters must be respected. Also, the imported string must be encapsulated with a Scope command. If it is a RuleSet address, it should be a valid address, and the engine should be initialized using RuleSet overload.

The behavior included will not be executed. In order to do so, the Call command must be used. The included source parameter can accept a ruleset address as well (e.g., ruleset://flow/main?parameter:name=CreateCaseOfficer)
Parameters

  1. source
    1. Description: Assigns a source that is imported by the command, as well as a variable name or ruleset address. If it is variable name, the variable type must be defined as a String or IElementModel.
    2. Mandatory: Yes
    3. Type: String (variable name)

Samples

For example, in Validation logic you can use the Include command to reference a loaded Logic. For more information please check here.

Creating runtime engine

You can use a RuntimeEngine to create an instance of an engine directly from a RuleSet address.

For example:

// Here we assume the ruleset is for path and filename addressing:
var engine = RuntimeEngine.FromRuleSet(rs, "Premiums DRD.xml");

Creating execution plans

All execution plans can be created using a RuleSet address.

For example:

var engine = RuntimeEngine.FromRuleSet(rs, "ruleset:///Premiums DRD");
var flow = new Flows.Flow(ruleset, "ruleset://Flows/Purchase/OrderRuleFlow");
Updated on July 19, 2019

Was this article helpful?