Loading models

◷ Reading Time: 6 minutes

Rules and logic are modeled in different languages (e.g., XML, SXP, Natural language, your custom DSL, etc.). All are stored in files, and those model files will be structured and stored into a physical container (i.e., File System Folders, Database tables, Cloud storages, etc.).

Physical address

All those models located in a physical container have some address associated with them. For example, if a file is located on your hard drive in a folder, then you have an address to access the file. For example:

C:\InventorySystem\Rules\Common\Common.xml
C:\InventorySystem\Rules\Dispatch.xml
C:\InventorySystem\Rules\Address.xml

Or you may store those rules in a file server or web server. Then there would be a physical address associated with them. For example:

ftp://InventorySystem/Rules/Common/Common.xml
ftp://InventorySystem/Rules/Dispatch.xml
ftp://InventorySystem/Rules/Address.xml

Depending on how you are hosting those models, you will have different protocol and address associated with each one.

Out-of-the-box, the following containers are supported:

  • File system
  • Network location
  • FTP
  • HTTP

In some scenarios, there is no direct and ready-to-use physical address for those models stored in a physical container (e.g., Storing models in cloud blob storage). What you need to do in such cases is to create a custom mechanism to access those containers.

Custom Physical Container

In order to access a custom physical container, you can implement an interface to create a connection and access to those models.

namespace FlexRule.Core.Model.SourceConnections
{
    /// <summary>
    /// An interface to a stream of source data is required to read a model.
    /// This interface would give the functionality required to access the source of a model
    /// </summary>
    public interface ISourceConnection
    {
        /// <summary>
        /// Opens a link to a physical location
        /// </summary>
        void Open();
 
        /// <summary>
        /// Retrieve a stream to read model content
        /// </summary>
        /// <returns></returns>
        Stream ReadSource();
 
        /// <summary>
        /// Close the stream and cleans up the resources
        /// </summary>
        void Close();
    }
}

Sample database repository

For example, let’s consider that we have a simple database with one table that stores the content of the rules. If you have implemented a DbSourceConnection like this:

class DbSourceConnection : ISourceConnection
{
    private readonly string _connectionString;
    private readonly string _modelName;
 
    /// <summary>
    /// Creates a database source connection
    /// </summary>
    /// <param name="connectionString">Databse connection string</param>
    /// <param name="modelName">name of model to be loaded from database</param>
    public DbSourceConnection(string connectionString, string modelName)
    {
        _connectionString = connectionString;
        _modelName = modelName;
    }
 
    public void Open()
    {
        // Opens database using connection string
        throw new NotImplementedException();
    }
 
    public Stream ReadSource()
    {
        // Reads model from table using field: _modelName 
        throw new NotImplementedException();
    }
 
    public void Close()
    {
        // Closes database 
        throw new NotImplementedException();
    }
}

Then you can use SourceConnectionReader to read the model like this:

var sourceConnection = new DbSourceConnection("YOUR DATABASE CONNECTION STRING", "Discount");
var sourceContent = new SourceConnectionReader(sourceConnection)
                    .ReadAllBytes(); // reads all the content as binary
if (sourceContent == null)
    throw new DeploymentPackageException("Error in reading source, source can not be empty or null");
 
// Loads model container using LoadAdapterUtility class
var modelContainer = LoadAdapterUtility.LoadNavigableSource(sourceContent);

In-memory address

When models are loaded into memory and stored in a IQueryableSet container like RuleSet, then each model can be accessed using a logical address. Those addresses start with a protocol named ruleset:// and locate a model in memory.

RuleSet is a programmatic group of multiple model containers already loaded into the application. RuleSet provides uniform resource locator (URL)–based addressing of each model. In each RuleSet, multiple ModelContainers can be stored, each able to have multiple rule and logic models. This grouping allows your engines (e.g., procedural, flow, and validation) to load logic and rules based on virtual addressing of a model inside a rule set. Therefore, in instantiating an engine, instead of passing an IElementModel, you can pass the RuleSet object and an address to load the model.

Loading

Loading a model is a process of converting models stored in a physical location to an in-memory and logical container that can be used to access them during Execution plan creation and logic execution.

Simple version

The simple version of loading is using LoadAdapterUtility to do all the plumbing and then fill or create your container with models.

Loading model directly from the source file or binary:

public static IElementModel LoadModel(string ruleUri)
public static IElementModel LoadModel(byte[] ruleContent)
Sample 1

Loading from string content:

IElementModel model = LoadAdapterUtility.LoadModel(Encoding.UTF8.GetBytes(rule));
Sample 2

Loading from file address:

IElementModel model = LoadAdapterUtility.LoadModel(@"C:\InventorySystem\Rules\Common\Common.xml");

Loading ModelContainer

A model container is a collection of IElementModels.

Loading model into ModelContainer:

public static void FillNavigableSource(ModelContainer container, string rule, string sectionName = null, string user = null, string userPassword = null)
public static ModelContainer LoadNavigableSource(string rule, string sectionName = null, string user = null, string userPassword = null)

When you have your custom source connection, you can use method ReadAllBytes() of class SourceConnectionReader to read your source and the use the following overload to create a model container:

public static ModelContainer LoadNavigableSource(byte[] rule, string sectionName = null)

Advanced Version

If you’re defining your own language or source connection you may need to consider a more advanced version of the loading process. This version includes connecting all the components you see in the diagram below.

  • In order to load a model from a physical container, you need to first create a connection using SourceConnectionBuilder.
var cnnBuilder = new SourceConnectionBuilder();
var sourceConnection = cnnBuilder.Build(rulePath);
  • Then you need to create a source provider and a load adapter
var sourceProvider = new CustomSourceProvider(sourceConnection);
var loadAdapter = new NavigableSourceLoadAdapter(sourceProvider);
  • And last is creating a model container and use the load adapter to fill the container with models
var mc = new ModelContainer();
loadAdapter.Load(mc, sectionName);
Updated on July 12, 2019

Was this article helpful?

Related Articles