Microsoft Rules Engine

repository·main·Indexed 25 days ago

https://github.com/microsoft/rulesengine

A .NET library designed to abstract business logic, rules, and policies away from core system logic. It allows developers to define rules using JSON schemas or programmatically in C# using lambda expressions, enabling updates to business logic without redeploying applications. Features include support for external storage (SQL, Cosmos DB, Azure Blob Storage), custom action registration, rule chaining via EvaluateRule, and a Blazor-based visual editor via the RulesEngineEditor package.

Tokens
4.7K
Snippets
10
Records
27
Agent score
87%

What's inside Rules Engine

  1. Understand the Rules Engine architecture

    main

    The Rules Engine is designed to decouple business logic from the core application code. It allows rules to change without requiring code re-compilation or redeployment.

    Key architectural components include:

    • Rules Engine: The core library/NuGet package that evaluates rules against inputs.
    • Rules Store: A storage mechanism for rules. While the engine requires rules to follow a specific schema, you can store them in any format or location (e.g., JSON files, Azure Blob Storage, Cosmos DB, or Azure App Configuration).
    • Input: Dynamic data provided to the engine. Inputs can be typed objects or ExpandoObject types, allowing for highly flexible data models.
    • Wrapper: A custom implementation layer created by the developer that fetches rules from the Rules Store, converts them into the required WorkflowRules structure, and passes them along with the Input to the Rules Engine.
  2. Extend expressions via custom class/type injection

    main
    You can inject custom static classes into the RulesEngine to perform complex operations that standard C# expressions cannot easily handle. This is done by adding the types to the CustomTypes array in ReSettings and passing that settings object to the RulesEngine constructor.
  3. Basic Usage of RulesEngine

    main

    To use RulesEngine, define your workflows in a JSON format containing WorkflowName and a list of Rules. Each rule has a RuleName and an Expression using C# syntax. Initialize the RulesEngine with these workflow rules and execute them using ExecuteAllRulesAsync by passing the workflow name and input objects.

    [
      {
        "WorkflowName": "Discount",
        "Rules": [
          {
            "RuleName": "GiveDiscount10",
            "Expression": "input1.country == \"india\" AND input1.loyalityFactor <= 2"
          }
        ]
      }
    ]
    var workflowRules = // Get list of workflow rules from JSON
    var re = new RulesEngine.RulesEngine(workflowRules);
    
    // Execute rules with inputs (can be concrete, anonymous, or dynamic)
    var resultList = await re.ExecuteAllRulesAsync("Discount", input1, input2, input3);
    
    foreach(var result in resultList){
      Console.WriteLine($"Rule - {result.Rule.RuleName}, IsSuccess - {result.IsSuccess}");
    }
  4. Use RulesEngine with JSON-defined workflows

    main

    You can define rules in a JSON format following the project's schema and then inject them into the engine. The engine uses ExecuteAllRulesAsync to evaluate an input object against a specific workflow.

    1. Define a JSON array of workflows containing Rules.
    2. Use RuleExpressionType: "LambdaExpression" for C# lambda-style expressions.
    3. Initialize the RulesEngine with the deserialized workflow objects.
    4. Call ExecuteAllRulesAsync(workflowName, input) to get a list of RuleResultTree objects indicating success or failure.
  5. Use the OutputExpression inbuilt action

    main

    The OutputExpression action evaluates a C# expression based on the provided RuleParameters and returns the result as the action's output. This is useful for calculating values (like discounts) when a rule succeeds or fails.

    To use it, define an OnSuccess or OnFailure action in your rule JSON with the name OutputExpression and provide the expression in the Context object.

    {
      "WorkflowName": "inputWorkflow",
      "Rules": [
        {
          "RuleName": "GiveDiscount10Percent",
          "SuccessEvent": "10",
          "ErrorMessage": "One or more adjust rules failed.",
          "RuleExpressionType": "LambdaExpression",
          "Expression": "input1.couy == \"india\" AND input1.loyalityFactor <= 2 AND input1.totalPurchasesToDate >= 5000 AND input2.totalOrders > 2 AND input2.noOfVisitsPerMonth > 2",
          "Actions": {
             "OnSuccess": {
                "Name": "OutputExpression",
                "Context": {
                   "Expression": "input1.TotalBilled * 0.9"
                }
             }
          }
        }
      ]
    }

    Execute the workflow and retrieve the output:

    var ruleResultList = await rulesEngine.ExecuteAllRulesAsync("inputWorkflow", ruleParameters);
    foreach(var ruleResult in ruleResultList){
       if(ruleResult.ActionResult != null){
           Console.WriteLine(ruleResult.ActionResult.Output); // Contains the evaluated value
       }
    }
  6. Implement and register custom actions

    main

    You can extend the engine by creating custom logic that runs after rule evaluation.

    1. Create the action: Inherit from ActionBase and override the Run method. The Run method can be synchronous or asynchronous (async ValueTask<object>).
    2. Register the action: Add the action to the CustomActions dictionary within an ReSettings object. Pass this settings object to the RulesEngine constructor.
    3. Use in JSON: Reference the registered name in the Actions section of your rule definition.
  7. Use the EvaluateRule inbuilt action for rule chaining

    main

    The EvaluateRule action allows you to chain rules together. When a rule executes, it can trigger another rule as an action. It supports:

    • Filtering inputs: Using inputFilter to pass only specific parameters to the chained rule.
    • Additional inputs: Using additionalInputs to pass new computed values to the chained rule.

    To execute a workflow that includes EvaluateRule, use ExecuteActionWorkflowAsync instead of ExecuteAllRulesAsync to ensure the action chain is processed.

    // Example of EvaluateRule with filtering and additional inputs
    "Actions": {
        "OnSuccess": {
            "Name": "EvaluateRule",
            "Context": {
                "workflowName": "inputWorkflow",
                "ruleName": "GiveDiscount10Percent",
                "inputFilter": ["input2"],
                "additionalInputs": [
                    {
                        "Name": "currentDiscount",
                        "Expression": "input1.TotalBilled * 0.9"
                    }
                ]
            }
        }
    }
    // Use ExecuteActionWorkflowAsync to trigger the action chain
    var result = await rulesEngine.ExecuteActionWorkflowAsync("inputWorkflow", "GiveDiscount20Percent", ruleParameters);
    Console.WriteLine(result.Output);