Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Abbasc52/optional fast compile #570

Merged
merged 4 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}
}


}
Loading