Skip to main content

How to solve "Rule config file is not present" issue in microsoft rules engine

 Introduction

Microsoft Rules Engine doing fantastic job in handling and evaluating rules and its expressions. Big Thank to its Authors for the mind-blowing invention to tackle workflow based rules and its evaluation on the fly. I am just an beginner for this package and initially tried with some predefined workflow evaluation in azure function. I came across with some weird issue "Rule config file is not present". In this article I will drive you to solve this kind of problem

Predefined Discount Workflow for evaluation

I just followed samples provided in https://microsoft.github.io/RulesEngine/

[
  {
    "WorkflowName": "Discount",
    "Rules": [
      {
        "RuleName": "GiveDiscount10",
        "Expression": "input1.country == \"india\" AND input1.loyalityFactor <= 2 AND input1.totalPurchasesToDate >= 5000 AND input2.totalOrders > 2 AND input3.noOfVisitsPerMonth > 2"
      },
      {
        "RuleName": "GiveDiscount20",
        "Expression": "input1.country == \"india\" AND input1.loyalityFactor == 3 AND input1.totalPurchasesToDate >= 10000 AND input2.totalOrders > 2 AND input3.noOfVisitsPerMonth > 2"
      }
    ]
  }
]

Issue Video


Code 

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
using RulesEngine.Models;
 
namespace EvaluateFunction
{
    public static class Function1
    {
        [FunctionName("Function1")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            bool result = false;
            try
            {
                string WorkflowName = req.Query["WorkflowName"];
                string rulename = req.Query["rulename"];
 
                var jsonRuleData = "[{\"WorkflowName\":\"Discount\",\"Rules\":[{\"RuleName\":\"GiveDiscount10\",\"Expression\":\"input1.country == \\\"india\\\" AND input1.loyalityFactor <= 2 AND input1.totalPurchasesToDate >= 5000 AND input2.totalOrders >2 AND input3.noOfVisitsPerMonth > 2\"},{\"RuleName\":\"GiveDiscount20\",\"Expression\":\"input1.country == \\\"india\\\" AND input1.loyalityFactor == 3 AND input1.totalPurchasesToDate >= 10000 AND input2.totalOrders > 2 AND input3.noOfVisitsPerMonth >2 \"}]}]";
                var workflowData = JsonConvert.DeserializeObject<List<Workflow>>(jsonRuleData);
                if (workflowData != null && workflowData.Any())
                {
                    var data = workflowData.Where(x => x.WorkflowName.ToLowerInvariant() == WorkflowName.ToLowerInvariant()).FirstOrDefault();
                    if (data != null)
                    {
                        var rules = data.Rules.Where(x => x.RuleName.ToLowerInvariant() == rulename.ToLowerInvariant());
                        if (rules != null && rules.Any())
                        {
                            var re = new RulesEngine.RulesEngine(workflowData.ToArray());
 
                            dynamic input1 = new { country = "india", loyalityFactor = 1, totalPurchasesToDate = 5002 };
                            dynamic input2 = new { totalOrders = 3 };
                            dynamic input3 = new { noOfVisitsPerMonth = 3 };
 
                            var rp1 = new RuleParameter("input1", input1);
                            var rp2 = new RuleParameter("input2", input2);
                            var rp3 = new RuleParameter("input3", input3);
 
                            var resultList = await re.ExecuteAllRulesAsync(WorkflowName, rp1, rp2, rp3);
                            if (resultList != null && resultList.Any())
                            {
                                var finalResult = resultList.Where(x => x.Rule.RuleName.ToLowerInvariant() == rulename.ToLowerInvariant()).FirstOrDefault().IsSuccess;
                                result = finalResult;
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                throw e;
            }
 
            return new OkObjectResult(result);
        }
    }
}
 

Code Walkthrough - Http Trigger Azure Function

For this article I have only predefined set of rules and we are evaluating that with fixed rules parameter values. Let's walkthrough the code

Get Query string values - Http Get Request

Let's assume that our HTTP Get Request is having two query string parameters
  1. WorkflowName
  2. RuleName

Get those query string values HttpRequest Objects

Convert Json Into C# Json string

To Evaluate predefined values we need to convert Discount workflow Json into C# Json string, we will get something like this

var jsonRuleData = "[{\"WorkflowName\":\"Discount\",\"Rules\":[{\"RuleName\":\"GiveDiscount10\",\"Expression\":\"input1.country == \\\"india\\\" AND input1.loyalityFactor <= 2 AND input1.totalPurchasesToDate >= 5000 AND input2.totalOrders >2 AND input3.noOfVisitsPerMonth > 2\"},{\"RuleName\":\"GiveDiscount20\",\"Expression\":\"input1.country == \\\"india\\\" AND input1.loyalityFactor == 3 AND input1.totalPurchasesToDate >= 10000 AND input2.totalOrders > 2 AND input3.noOfVisitsPerMonth >2 \"}]}]";

Deserialize Json string to WorkFlow List Object

Microsoft Rules Engine is built with some sophisticated inbuit models. Let's try to deserialize json 
string Workflow list object

Construct Rules Engine Object

After doing all our serialization and deserilaization process. Let's try to instantiate our RulesEngine Object with workflow array list.

Predefined Rules Parameter

Let's construct rules parameter based on discount workflow rules expressions. These expressions have three primary fields
  1. input1
  2. input2
  3. input3 
These primary fields will have their own subordinate or secondary expression fields.

input1 rule parameter has following fields
  1. country
  2. loyalityFactor
  3. totalPurchasesToDate
input2  rule parameter has the following
  1. totalOrders
input3 rule parameter has the following
  1. noOfVisitsPerMonth

Execute Discount Workflow Rules

Let's execute RulesEngine object with ExecuteAllRulesAsync method primarily carries workflow array object and input rule parameter to evaluate all rules and its expression and will return RuleResultTree list object
var resultList = await re.ExecuteAllRulesAsync(WorkflowName, rp1, rp2, rp3);

Issue Root Cause

I Initially found that Rules engine is case sensitive. Discount workflow name is having Pascal case but I provided the value which is in the form of camel case. Expected workflow name value is Discount but provided discount as workflow name. That's why we got the issue "Rule config file is not present" not present.

Solution

We should provide exact Workflow or rulename as provided in Config file in our case json string will fix the issue.

Conclusion

I provided the proper workflow name and then rules engine evaluated and its provided the result as expected. Hope this article is helpful to you.


Comments

Popular posts from this blog

How to resolve ASP.NET core web API 2 mins timeout issue

Introduction We are in the new world of microservices and cross-platform applications which will be supported for multiple platforms and multiple heterogeneous teams can work on the same application. I like ASP.NET Core by the way its groomed to support modern architecture and adhere to the software principles. I am a big fan of dot net and now I become the craziest fan after seeing the sophisticated facility by dot net core to support infrastructure level where we can easily perform vertical and horizontal scaling. It very important design aspect is to keep things simple and short and by the way, RESTFul applications are build and it is a powerful mantra for REST-based application and frameworks. Some times we need to overrule some principles and order to handle some situations. I would like to share my situation of handling HTTP long polling to resolve the ASP.Net core 2 mins issue. What is HTTP Long polling? In the RESTFul term, when a client asks for a query from the serv

How to Resolve ASP.NET Core Key Protection Ring Problem in AWS Lambda

Introduction When it comes to server less web application design using asp.net core razor pages, we definitely need to consider a factor of data protection key management and its lifetime in asp.net core. I developed a site using AWS toolkit of ASP.NET Core Razor Pages. The main advantage of ASP.NET Core is cross-platform from where we can deploy our application in MAC, Linux or windows. I deployed my site initially in IIS Server from which I got the results as expected .but later period I decided to host my site in AWS Lambda in order to meet our client requirement. Strangely, I got unexpected behavior from my site. I just refer the cloud information Lambda Log to identify or pinpoint the case, I got the error Information like “Error Unprotecting the session cookie” from the log. In this article, I tried to explain the root cause of the problem and its solution to overcome such kind of issue. Data Protection in ASP.NET Core This is feature in ASP.NET Core which acts as repl

Which linq method performs better: Where(expression).FirstorDefault() vs .FirstOrDefault(expression)

 Introduction When it comes to LINQ, we always have multiple options to execute the query for the same scenario. Choosing correct one is always challenging aspect and debatable one. In one of our previous articles   Any Vs Count  , we have done performance testing about best LINQ methods over .NET types. In this article, I would like to share about  Where(expression).FirstorDefault() vs .FirstOrDefault(expression) Approaches Performance testing for  Where(expression).FirstorDefault() vs .FirstOrDefault(expression) is very interesting IEnumerable<T> or ICollcetion<T>  .FirstOrDefault(expression) is better than  Where(expression).FirstorDefault() Public API To check the performance, I need some amount of data which should already available. So I decided to choose this  public api . Thanks to publicapis Public API Models Entry class using System ; using System.Collections.Generic ; using System.Text ;   namespace AnyVsCount { public class Entry { pub