Skip to content

Commit 22353a3

Browse files
authored
Abbasc52/optional fast compile (#570)
* added setting to turn off fast compilation * added test cases and docs on ReSettings * added ruleparameter.create method * updated CHANGELOG
1 parent 9bcf4f3 commit 22353a3

7 files changed

Lines changed: 77 additions & 13 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ All notable changes to this project will be documented in this file.
55
## [5.0.3]
66
- Updated dependencies to latest
77
- Fixed RulesEngine throwing exception when type name is same as input name
8+
- Added config to disable FastCompile for expressions
9+
- Added RuleParameter.Create method for better handling on types when value is null
810

911
## [5.0.2]
1012
- Fixed Scoped Params returning incorrect results in some corner case scenarios

docs/index.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ RulesEngine is a highly extensible library to build rule based system using C# e
3737
- [Steps to use a custom Action](#steps-to-use-a-custom-action)
3838
- [Standalone Expression Evaluator](#standalone-expression-evaluator)
3939
- [Usage](#usage-2)
40+
- [Settings](#settings)
41+
- [NestedRuleExecutionMode](#nestedruleexecutionmode)
4042

4143

4244

@@ -555,3 +557,29 @@ This will output "Hello World"
555557

556558
For more advanced usage, refer - https://dotnetfiddle.net/KSX8i0
557559
560+
## Settings
561+
RulesEngine allows you to pass optional `ReSettings` in constructor to specify certain configuration for RulesEngine.
562+
563+
Here are the all the options available:-
564+
565+
566+
| Property | Type | Default Value | Description |
567+
| --- | --- | --- | --- |
568+
| `CustomTypes` | `Type[]` | N/A | Custom types to be used in rule expressions. |
569+
| `CustomActions` | `Dictionary<string, Func<ActionBase>>` | N/A | Custom actions that can be used in the rules. |
570+
| `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`. |
571+
| `IgnoreException` | `bool` | `false` | If `true`, it will ignore any exception thrown with rule compilation/execution. |
572+
| `EnableFormattedErrorMessage` | `bool` | `true` | Enables error message formatting. |
573+
| `EnableScopedParams` | `bool` | `true` | Enables global parameters and local parameters for rules. |
574+
| `IsExpressionCaseSensitive` | `bool` | `false` | Sets whether expressions are case sensitive. |
575+
| `AutoRegisterInputType` | `bool` | `true` | Auto registers input type in custom type to allow calling method on type. |
576+
| `NestedRuleExecutionMode` | `NestedRuleExecutionMode` | `All` | Sets the mode for nested rule execution. |
577+
| `CacheConfig` | `MemCacheConfig` | N/A | Configures the memory cache. |
578+
| `UseFastExpressionCompiler` | `bool` | `true` | Whether to use FastExpressionCompiler for rule compilation. |
579+
580+
581+
### NestedRuleExecutionMode
582+
| Value | Description |
583+
| --- | --- |
584+
| `All` | Executes all nested rules. |
585+
| `Performance` | Skips nested rules whose execution does not impact parent rule's result. |

src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,19 @@ public Func<object[], T> Compile<T>(string expression, RuleParameter[] ruleParam
5757
}
5858
var expressionBody = new List<Expression>() { e };
5959
var wrappedExpression = WrapExpression<T>(expressionBody, parameterExpressions, new ParameterExpression[] { });
60-
return wrappedExpression.CompileFast();
60+
return CompileExpression(wrappedExpression);
6161

6262
}
6363

64+
private Func<object[], T> CompileExpression<T>(Expression<Func<object[], T>> expression)
65+
{
66+
if(_reSettings.UseFastExpressionCompiler)
67+
{
68+
return expression.CompileFast();
69+
}
70+
return expression.Compile();
71+
}
72+
6473
private Expression<Func<object[], T>> WrapExpression<T>(List<Expression> expressionList, ParameterExpression[] parameters, ParameterExpression[] variables)
6574
{
6675
var argExp = Expression.Parameter(typeof(object[]), "args");
@@ -77,7 +86,7 @@ internal Func<object[],Dictionary<string,object>> CompileRuleExpressionParameter
7786
{
7887
ruleExpParams = ruleExpParams ?? new RuleExpressionParameter[] { };
7988
var expression = CreateDictionaryExpression(ruleParams, ruleExpParams);
80-
return expression.CompileFast();
89+
return CompileExpression(expression);
8190
}
8291

8392
public T Evaluate<T>(string expression, RuleParameter[] ruleParams)

src/RulesEngine/Models/ReSettings.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ internal ReSettings(ReSettings reSettings)
2727
CacheConfig = reSettings.CacheConfig;
2828
IsExpressionCaseSensitive = reSettings.IsExpressionCaseSensitive;
2929
AutoRegisterInputType = reSettings.AutoRegisterInputType;
30-
}
30+
UseFastExpressionCompiler = reSettings.UseFastExpressionCompiler;
31+
}
3132

3233

3334
/// <summary>
@@ -79,6 +80,10 @@ internal ReSettings(ReSettings reSettings)
7980
/// </summary>
8081
public NestedRuleExecutionMode NestedRuleExecutionMode { get; set; } = NestedRuleExecutionMode.All;
8182
public MemCacheConfig CacheConfig { get; set; }
83+
/// <summary>
84+
/// Whether to use FastExpressionCompiler for rule compilation
85+
/// </summary>
86+
public bool UseFastExpressionCompiler { get; set; } = true;
8287
}
8388

8489
public enum NestedRuleExecutionMode

src/RulesEngine/Models/RuleParameter.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@ public RuleParameter(string name, object value)
1717
Init(name, Value?.GetType());
1818
}
1919

20-
internal RuleParameter(string name, Type type)
20+
21+
internal RuleParameter(string name, Type type,object value = null)
2122
{
23+
Value = Utils.GetTypedObject(value);
2224
Init(name, type);
2325
}
26+
2427
public Type Type { get; private set; }
2528
public string Name { get; private set; }
2629
public object Value { get; private set; }
@@ -33,5 +36,13 @@ private void Init(string name, Type type)
3336
ParameterExpression = Expression.Parameter(Type, Name);
3437
}
3538

39+
public static RuleParameter Create<T>(string name, T value)
40+
{
41+
var typedValue = Utils.GetTypedObject(value);
42+
var type = typedValue?.GetType() ?? typeof(T);
43+
return new RuleParameter(name,type,value);
44+
}
45+
46+
3647
}
3748
}

test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -338,8 +338,9 @@ public async Task ExecuteRule_InputWithVariableProps_ReturnsResult(string ruleFi
338338
}
339339

340340
[Theory]
341-
[InlineData("rules4.json")]
342-
public async Task RulesEngine_Execute_Rule_For_Nested_Rule_Params_Returns_Success(string ruleFileName)
341+
[InlineData("rules4.json", true)]
342+
[InlineData("rules4.json", false)]
343+
public async Task RulesEngine_Execute_Rule_For_Nested_Rule_Params_Returns_Success(string ruleFileName,bool fastExpressionEnabled)
343344
{
344345
var inputs = GetInputs4();
345346

@@ -359,7 +360,9 @@ public async Task RulesEngine_Execute_Rule_For_Nested_Rule_Params_Returns_Succes
359360
}
360361

361362
var fileData = File.ReadAllText(files[0]);
362-
var bre = new RulesEngine(JsonConvert.DeserializeObject<Workflow[]>(fileData), null);
363+
var bre = new RulesEngine(JsonConvert.DeserializeObject<Workflow[]>(fileData), new ReSettings {
364+
UseFastExpressionCompiler = fastExpressionEnabled
365+
});
363366
var result = await bre.ExecuteAllRulesAsync("inputWorkflow", ruleParams?.ToArray());
364367
var ruleResult = result?.FirstOrDefault(r => string.Equals(r.Rule.RuleName, "GiveDiscount10", StringComparison.OrdinalIgnoreCase));
365368
Assert.True(ruleResult.IsSuccess);

test/RulesEngine.UnitTest/RuleExpressionParserTests/RuleExpressionParserTests.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,8 @@
33

44
using Newtonsoft.Json.Linq;
55
using RulesEngine.ExpressionBuilders;
6-
using System;
7-
using System.Collections.Generic;
86
using System.Diagnostics.CodeAnalysis;
9-
using System.Linq;
10-
using System.Text;
11-
using System.Threading.Tasks;
127
using Xunit;
13-
using System.Text.Json;
148

159
namespace RulesEngine.UnitTest.RuleExpressionParserTests
1610
{
@@ -59,5 +53,17 @@ public void TestExpressionWithJObject()
5953

6054
Assert.Equal("helloworld", value3);
6155
}
56+
57+
[Theory]
58+
[InlineData(false)]
59+
public void TestExpressionWithDifferentCompilerSettings(bool fastExpressionEnabled){
60+
var ruleParser = new RuleExpressionParser(new Models.ReSettings() { UseFastExpressionCompiler = fastExpressionEnabled });
61+
62+
decimal? d1 = null;
63+
var result = ruleParser.Evaluate<bool>("d1 < 20", new[] { Models.RuleParameter.Create("d1", d1) });
64+
Assert.False(result);
65+
}
6266
}
67+
68+
6369
}

0 commit comments

Comments
 (0)