Skip to content

Commit

Permalink
Abbasc52/optional fast compile (#570)
Browse files Browse the repository at this point in the history
* added setting to turn off fast compilation

* added test cases and docs on ReSettings

* added ruleparameter.create method

* updated CHANGELOG
  • Loading branch information
abbasc52 authored Jan 12, 2024
1 parent 9bcf4f3 commit 22353a3
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 13 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ All notable changes to this project will be documented in this file.
## [5.0.3]
- Updated dependencies to latest
- Fixed RulesEngine throwing exception when type name is same as input name
- Added config to disable FastCompile for expressions
- Added RuleParameter.Create method for better handling on types when value is null

## [5.0.2]
- Fixed Scoped Params returning incorrect results in some corner case scenarios
Expand Down
28 changes: 28 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ RulesEngine is a highly extensible library to build rule based system using C# e
- [Steps to use a custom Action](#steps-to-use-a-custom-action)
- [Standalone Expression Evaluator](#standalone-expression-evaluator)
- [Usage](#usage-2)
- [Settings](#settings)
- [NestedRuleExecutionMode](#nestedruleexecutionmode)



Expand Down Expand Up @@ -555,3 +557,29 @@ This will output "Hello World"

For more advanced usage, refer - https://dotnetfiddle.net/KSX8i0
## Settings
RulesEngine allows you to pass optional `ReSettings` in constructor to specify certain configuration for RulesEngine.

Here are the all the options available:-


| Property | Type | Default Value | Description |
| --- | --- | --- | --- |
| `CustomTypes` | `Type[]` | N/A | Custom types to be used in rule expressions. |
| `CustomActions` | `Dictionary<string, Func<ActionBase>>` | N/A | Custom actions that can be used in the rules. |
| `EnableExceptionAsErrorMessage` | `bool` | `true` | If `true`, returns any exception occurred while rule execution as an error message. Otherwise, throws an exception. This setting is only applicable if `IgnoreException` is set to `false`. |
| `IgnoreException` | `bool` | `false` | If `true`, it will ignore any exception thrown with rule compilation/execution. |
| `EnableFormattedErrorMessage` | `bool` | `true` | Enables error message formatting. |
| `EnableScopedParams` | `bool` | `true` | Enables global parameters and local parameters for rules. |
| `IsExpressionCaseSensitive` | `bool` | `false` | Sets whether expressions are case sensitive. |
| `AutoRegisterInputType` | `bool` | `true` | Auto registers input type in custom type to allow calling method on type. |
| `NestedRuleExecutionMode` | `NestedRuleExecutionMode` | `All` | Sets the mode for nested rule execution. |
| `CacheConfig` | `MemCacheConfig` | N/A | Configures the memory cache. |
| `UseFastExpressionCompiler` | `bool` | `true` | Whether to use FastExpressionCompiler for rule compilation. |


### NestedRuleExecutionMode
| Value | Description |
| --- | --- |
| `All` | Executes all nested rules. |
| `Performance` | Skips nested rules whose execution does not impact parent rule's result. |
13 changes: 11 additions & 2 deletions src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,19 @@ public Func<object[], T> Compile<T>(string expression, RuleParameter[] ruleParam
}
var expressionBody = new List<Expression>() { e };
var wrappedExpression = WrapExpression<T>(expressionBody, parameterExpressions, new ParameterExpression[] { });
return wrappedExpression.CompileFast();
return CompileExpression(wrappedExpression);

}

private Func<object[], T> CompileExpression<T>(Expression<Func<object[], T>> expression)
{
if(_reSettings.UseFastExpressionCompiler)
{
return expression.CompileFast();
}
return expression.Compile();
}

private Expression<Func<object[], T>> WrapExpression<T>(List<Expression> expressionList, ParameterExpression[] parameters, ParameterExpression[] variables)
{
var argExp = Expression.Parameter(typeof(object[]), "args");
Expand All @@ -77,7 +86,7 @@ internal Func<object[],Dictionary<string,object>> CompileRuleExpressionParameter
{
ruleExpParams = ruleExpParams ?? new RuleExpressionParameter[] { };
var expression = CreateDictionaryExpression(ruleParams, ruleExpParams);
return expression.CompileFast();
return CompileExpression(expression);
}

public T Evaluate<T>(string expression, RuleParameter[] ruleParams)
Expand Down
7 changes: 6 additions & 1 deletion src/RulesEngine/Models/ReSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ internal ReSettings(ReSettings reSettings)
CacheConfig = reSettings.CacheConfig;
IsExpressionCaseSensitive = reSettings.IsExpressionCaseSensitive;
AutoRegisterInputType = reSettings.AutoRegisterInputType;
}
UseFastExpressionCompiler = reSettings.UseFastExpressionCompiler;
}


/// <summary>
Expand Down Expand Up @@ -79,6 +80,10 @@ internal ReSettings(ReSettings reSettings)
/// </summary>
public NestedRuleExecutionMode NestedRuleExecutionMode { get; set; } = NestedRuleExecutionMode.All;
public MemCacheConfig CacheConfig { get; set; }
/// <summary>
/// Whether to use FastExpressionCompiler for rule compilation
/// </summary>
public bool UseFastExpressionCompiler { get; set; } = true;
}

public enum NestedRuleExecutionMode
Expand Down
13 changes: 12 additions & 1 deletion src/RulesEngine/Models/RuleParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@ public RuleParameter(string name, object value)
Init(name, Value?.GetType());
}

internal RuleParameter(string name, Type type)

internal RuleParameter(string name, Type type,object value = null)
{
Value = Utils.GetTypedObject(value);
Init(name, type);
}

public Type Type { get; private set; }
public string Name { get; private set; }
public object Value { get; private set; }
Expand All @@ -33,5 +36,13 @@ private void Init(string name, Type type)
ParameterExpression = Expression.Parameter(Type, Name);
}

public static RuleParameter Create<T>(string name, T value)
{
var typedValue = Utils.GetTypedObject(value);
var type = typedValue?.GetType() ?? typeof(T);
return new RuleParameter(name,type,value);
}


}
}
9 changes: 6 additions & 3 deletions test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -338,8 +338,9 @@ public async Task ExecuteRule_InputWithVariableProps_ReturnsResult(string ruleFi
}

[Theory]
[InlineData("rules4.json")]
public async Task RulesEngine_Execute_Rule_For_Nested_Rule_Params_Returns_Success(string ruleFileName)
[InlineData("rules4.json", true)]
[InlineData("rules4.json", false)]
public async Task RulesEngine_Execute_Rule_For_Nested_Rule_Params_Returns_Success(string ruleFileName,bool fastExpressionEnabled)
{
var inputs = GetInputs4();

Expand All @@ -359,7 +360,9 @@ public async Task RulesEngine_Execute_Rule_For_Nested_Rule_Params_Returns_Succes
}

var fileData = File.ReadAllText(files[0]);
var bre = new RulesEngine(JsonConvert.DeserializeObject<Workflow[]>(fileData), null);
var bre = new RulesEngine(JsonConvert.DeserializeObject<Workflow[]>(fileData), new ReSettings {
UseFastExpressionCompiler = fastExpressionEnabled
});
var result = await bre.ExecuteAllRulesAsync("inputWorkflow", ruleParams?.ToArray());
var ruleResult = result?.FirstOrDefault(r => string.Equals(r.Rule.RuleName, "GiveDiscount10", StringComparison.OrdinalIgnoreCase));
Assert.True(ruleResult.IsSuccess);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,8 @@

using Newtonsoft.Json.Linq;
using RulesEngine.ExpressionBuilders;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;
using System.Text.Json;

namespace RulesEngine.UnitTest.RuleExpressionParserTests
{
Expand Down Expand Up @@ -59,5 +53,17 @@ public void TestExpressionWithJObject()

Assert.Equal("helloworld", value3);
}

[Theory]
[InlineData(false)]
public void TestExpressionWithDifferentCompilerSettings(bool fastExpressionEnabled){
var ruleParser = new RuleExpressionParser(new Models.ReSettings() { UseFastExpressionCompiler = fastExpressionEnabled });

decimal? d1 = null;
var result = ruleParser.Evaluate<bool>("d1 < 20", new[] { Models.RuleParameter.Create("d1", d1) });
Assert.False(result);
}
}


}

0 comments on commit 22353a3

Please sign in to comment.