From 2c26f6590a29ffe24459c1eb5d38074d6ba5a41b Mon Sep 17 00:00:00 2001 From: Israel Garcia Date: Fri, 7 Oct 2022 16:57:11 -0400 Subject: [PATCH 1/7] EvaluateRuleActions : Allows to track the results of a bifusrcation in case of OnEvaluateRule action. RuleCompiler; Thee is an issue when a globalParam is declared, it tries to be added to the input of each rule and subrules failed because it is already there. RuleEngine: The PostActions are executed at the end of the exetuon of every rule, not at the end of the workflow. --- src/RulesEngine/Actions/EvaluateRuleAction.cs | 4 +-- src/RulesEngine/HelperFunctions/Utils.cs | 17 +++++++++++ src/RulesEngine/RuleCompiler.cs | 6 ++-- src/RulesEngine/RulesEngine.cs | 28 ++++++++++++------- src/RulesEngine/RulesEngine.csproj | 4 +-- 5 files changed, 43 insertions(+), 16 deletions(-) diff --git a/src/RulesEngine/Actions/EvaluateRuleAction.cs b/src/RulesEngine/Actions/EvaluateRuleAction.cs index 032308e8..1e803568 100644 --- a/src/RulesEngine/Actions/EvaluateRuleAction.cs +++ b/src/RulesEngine/Actions/EvaluateRuleAction.cs @@ -26,10 +26,10 @@ internal async override ValueTask ExecuteAndReturnResultAsync( var innerResult = await base.ExecuteAndReturnResultAsync(context, ruleParameters, includeRuleResults); var output = innerResult.Output as ActionRuleResult; List resultList = null; - if (includeRuleResults) + if (includeRuleResults || output?.Results?.Count > 0) { resultList = new List(output?.Results ?? new List() { }); - resultList.AddRange(innerResult.Results); + if (innerResult.Results?.Count() > 0) resultList.AddRange(innerResult.Results); } return new ActionRuleResult { Output = output?.Output, diff --git a/src/RulesEngine/HelperFunctions/Utils.cs b/src/RulesEngine/HelperFunctions/Utils.cs index 4ae9262a..49f4c515 100644 --- a/src/RulesEngine/HelperFunctions/Utils.cs +++ b/src/RulesEngine/HelperFunctions/Utils.cs @@ -12,6 +12,23 @@ namespace RulesEngine.HelperFunctions { public static class Utils { + /* valid only for netstandard 2.0 */ +#if NETSTANDARD2_0 + + public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector) + { + HashSet seenKeys = new HashSet(); + foreach (TSource element in source) + { + if (seenKeys.Add(keySelector(element))) + { + yield return element; + } + } + } +#endif + /* **************************** */ + public static object GetTypedObject(dynamic input) { if (input is ExpandoObject) diff --git a/src/RulesEngine/RuleCompiler.cs b/src/RulesEngine/RuleCompiler.cs index 2c3f33a6..68a153ca 100644 --- a/src/RulesEngine/RuleCompiler.cs +++ b/src/RulesEngine/RuleCompiler.cs @@ -83,7 +83,7 @@ private RuleFunc GetDelegateForRule(Rule rule, RuleParameter[] r var scopedParamList = GetRuleExpressionParameters(rule.RuleExpressionType, rule?.LocalParams, ruleParams); var extendedRuleParams = ruleParams.Concat(scopedParamList.Select(c => new RuleParameter(c.ParameterExpression.Name, c.ParameterExpression.Type))) - .ToArray(); + .DistinctBy(e => e.Name).ToArray(); RuleFunc ruleFn; @@ -250,7 +250,7 @@ private RuleFunc GetWrappedRuleFunc(Rule rule, RuleFunc e.Name); var result = ruleFunc(extendedInputs.ToArray()); return result; }; @@ -260,5 +260,7 @@ private RuleExpressionBuilderBase GetExpressionBuilder(RuleExpressionType expres { return _expressionBuilderFactory.RuleGetExpressionBuilder(expressionType); } + + } } diff --git a/src/RulesEngine/RulesEngine.cs b/src/RulesEngine/RulesEngine.cs index 1a7d62e2..9a10b53a 100644 --- a/src/RulesEngine/RulesEngine.cs +++ b/src/RulesEngine/RulesEngine.cs @@ -103,8 +103,8 @@ public async ValueTask> ExecuteAllRulesAsync(string workflo { var sortedRuleParams = ruleParams.ToList(); sortedRuleParams.Sort((RuleParameter a, RuleParameter b) => string.Compare(a.Name, b.Name)); - var ruleResultList = ValidateWorkflowAndExecuteRule(workflowName, sortedRuleParams.ToArray()); - await ExecuteActionAsync(ruleResultList); + var ruleResultList = await ValidateWorkflowAndExecuteRule(workflowName, sortedRuleParams.ToArray()); + //await ExecuteActionAsync(ruleResultList); // Actions now are executed after each rule. return ruleResultList; } @@ -117,13 +117,20 @@ private async ValueTask ExecuteActionAsync(IEnumerable ruleResul await ExecuteActionAsync(ruleResult.ChildResults); } var actionResult = await ExecuteActionForRuleResult(ruleResult, false); - ruleResult.ActionResult = new ActionResult { - Output = actionResult.Output, - Exception = actionResult.Exception - }; + ruleResult.ActionResult = actionResult; } } + private async ValueTask ExecuteActionAsync(RuleResultTree ruleResult) + { + if (ruleResult.ChildResults != null) + { + await ExecuteActionAsync(ruleResult.ChildResults); + } + var actionResult = await ExecuteActionForRuleResult(ruleResult, false); + ruleResult.ActionResult = actionResult; + } + public async ValueTask ExecuteActionWorkflowAsync(string workflowName, string ruleName, RuleParameter[] ruleParameters) { var compiledRule = CompileRule(workflowName, ruleName, ruleParameters); @@ -250,13 +257,13 @@ public void RemoveWorkflow(params string[] workflowNames) /// input /// workflow name /// list of rule result set - private List ValidateWorkflowAndExecuteRule(string workflowName, RuleParameter[] ruleParams) + private async Task> ValidateWorkflowAndExecuteRule(string workflowName, RuleParameter[] ruleParams) { List result; if (RegisterRule(workflowName, ruleParams)) { - result = ExecuteAllRuleByWorkflow(workflowName, ruleParams); + result = await ExecuteAllRuleByWorkflow(workflowName, ruleParams); } else { @@ -329,13 +336,14 @@ private RuleFunc CompileRule(Rule rule, RuleParameter[] rulePara /// /// /// list of rule result set - private List ExecuteAllRuleByWorkflow(string workflowName, RuleParameter[] ruleParameters) + private async Task> ExecuteAllRuleByWorkflow(string workflowName, RuleParameter[] ruleParameters) { var result = new List(); var compiledRulesCacheKey = GetCompiledRulesKey(workflowName, ruleParameters); foreach (var compiledRule in _rulesCache.GetCompiledRules(compiledRulesCacheKey)?.Values) - { + { var resultTree = compiledRule(ruleParameters); + await ExecuteActionAsync(resultTree); result.Add(resultTree); } diff --git a/src/RulesEngine/RulesEngine.csproj b/src/RulesEngine/RulesEngine.csproj index 6140d516..bd88a69a 100644 --- a/src/RulesEngine/RulesEngine.csproj +++ b/src/RulesEngine/RulesEngine.csproj @@ -2,7 +2,7 @@ net6.0;netstandard2.0 - 4.0.0 + 4.1.0 Copyright (c) Microsoft Corporation. LICENSE https://github.com/microsoft/RulesEngine @@ -36,7 +36,7 @@ - + From 2c5864ad5ec85304794c1ff5e0fa94217bb5d679 Mon Sep 17 00:00:00 2001 From: Israel Garcia Date: Fri, 7 Oct 2022 23:29:05 -0400 Subject: [PATCH 2/7] save ExceptionMessage when an error occur in a inner rule --- src/RulesEngine/Actions/EvaluateRuleAction.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/RulesEngine/Actions/EvaluateRuleAction.cs b/src/RulesEngine/Actions/EvaluateRuleAction.cs index 1e803568..c2e52d25 100644 --- a/src/RulesEngine/Actions/EvaluateRuleAction.cs +++ b/src/RulesEngine/Actions/EvaluateRuleAction.cs @@ -26,6 +26,15 @@ internal async override ValueTask ExecuteAndReturnResultAsync( var innerResult = await base.ExecuteAndReturnResultAsync(context, ruleParameters, includeRuleResults); var output = innerResult.Output as ActionRuleResult; List resultList = null; + + if (innerResult.Exception != null) + { + foreach(var result in innerResult.Results) + { + result.ExceptionMessage += innerResult.Exception.Message; + } + } + if (includeRuleResults || output?.Results?.Count > 0) { resultList = new List(output?.Results ?? new List() { }); From e842e94e8d86f00db0e29a35acb8700561c3f382 Mon Sep 17 00:00:00 2001 From: Israel Garcia Date: Wed, 12 Oct 2022 11:32:59 -0400 Subject: [PATCH 3/7] add Contains Key --- src/RulesEngine/Actions/ActionContext.cs | 5 +++++ src/RulesEngine/Actions/EvaluateRuleAction.cs | 8 -------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/RulesEngine/Actions/ActionContext.cs b/src/RulesEngine/Actions/ActionContext.cs index af66cdc8..fe28e3fe 100644 --- a/src/RulesEngine/Actions/ActionContext.cs +++ b/src/RulesEngine/Actions/ActionContext.cs @@ -41,6 +41,11 @@ public RuleResultTree GetParentRuleResult() return _parentResult; } + public bool ContainsKey(string name) + { + return _context.ContainsKey(name); + } + public bool TryGetContext(string name,out T output) { try diff --git a/src/RulesEngine/Actions/EvaluateRuleAction.cs b/src/RulesEngine/Actions/EvaluateRuleAction.cs index c2e52d25..0017fb41 100644 --- a/src/RulesEngine/Actions/EvaluateRuleAction.cs +++ b/src/RulesEngine/Actions/EvaluateRuleAction.cs @@ -27,14 +27,6 @@ internal async override ValueTask ExecuteAndReturnResultAsync( var output = innerResult.Output as ActionRuleResult; List resultList = null; - if (innerResult.Exception != null) - { - foreach(var result in innerResult.Results) - { - result.ExceptionMessage += innerResult.Exception.Message; - } - } - if (includeRuleResults || output?.Results?.Count > 0) { resultList = new List(output?.Results ?? new List() { }); From f8ddfdfcba471c7d224ad00663727ce8bab0ea12 Mon Sep 17 00:00:00 2001 From: Israel Garcia Date: Fri, 24 Feb 2023 00:54:06 -0500 Subject: [PATCH 4/7] add aviva nuget --- src/RulesEngine/nuget.config | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/RulesEngine/nuget.config diff --git a/src/RulesEngine/nuget.config b/src/RulesEngine/nuget.config new file mode 100644 index 00000000..71441d49 --- /dev/null +++ b/src/RulesEngine/nuget.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file From 8f214a929db500db2d582d76f0a526a064edfd0c Mon Sep 17 00:00:00 2001 From: Israel Garcia Date: Sun, 1 Oct 2023 01:11:28 -0400 Subject: [PATCH 5/7] demo testing different scenarios --- demo/DemoApp/DemoApp.csproj | 2 +- demo/DemoApp/NestedInputDemo.cs | 16 +++++++++++++++- demo/DemoApp/Program.cs | 6 +++--- demo/DemoApp/Workflows/NestedInputDemo.json | 14 ++++++++++++++ .../ExpressionBuilders/RuleExpressionParser.cs | 2 +- src/RulesEngine/RuleCompiler.cs | 2 +- 6 files changed, 35 insertions(+), 7 deletions(-) diff --git a/demo/DemoApp/DemoApp.csproj b/demo/DemoApp/DemoApp.csproj index fb6e5d91..0b3c2182 100644 --- a/demo/DemoApp/DemoApp.csproj +++ b/demo/DemoApp/DemoApp.csproj @@ -16,7 +16,7 @@ PreserveNewest - PreserveNewest + Always diff --git a/demo/DemoApp/NestedInputDemo.cs b/demo/DemoApp/NestedInputDemo.cs index 9427af14..b8b7d0ba 100644 --- a/demo/DemoApp/NestedInputDemo.cs +++ b/demo/DemoApp/NestedInputDemo.cs @@ -51,7 +51,11 @@ public void Run() var fileData = File.ReadAllText(files[0]); var Workflows = JsonConvert.DeserializeObject>(fileData); - var bre = new RulesEngine.RulesEngine(Workflows.ToArray(), null); + var executionRules = new ReSettings(); + executionRules.NestedRuleExecutionMode = NestedRuleExecutionMode.Performance; + executionRules.CustomTypes = new Type[] { typeof(pep) }; + + var bre = new RulesEngine.RulesEngine(Workflows.ToArray(), executionRules); foreach (var workflow in Workflows) { var resultList = bre.ExecuteAllRulesAsync(workflow.WorkflowName, nestedInput).Result; @@ -63,8 +67,18 @@ public void Run() }); } + } + public class pep + { + public string Key; + public object Value; + public pep(string key, object value) + { + this.Key = key; + this.Value = value; + } } } } \ No newline at end of file diff --git a/demo/DemoApp/Program.cs b/demo/DemoApp/Program.cs index fe87de6d..4c792331 100644 --- a/demo/DemoApp/Program.cs +++ b/demo/DemoApp/Program.cs @@ -7,10 +7,10 @@ public static class Program { public static void Main(string[] args) { - new BasicDemo().Run(); - new JSONDemo().Run(); + //new BasicDemo().Run(); + //new JSONDemo().Run(); new NestedInputDemo().Run(); - new EFDemo().Run(); + //new EFDemo().Run(); } } } diff --git a/demo/DemoApp/Workflows/NestedInputDemo.json b/demo/DemoApp/Workflows/NestedInputDemo.json index 8c043843..1bdf901d 100644 --- a/demo/DemoApp/Workflows/NestedInputDemo.json +++ b/demo/DemoApp/Workflows/NestedInputDemo.json @@ -1,6 +1,20 @@ [ { "WorkflowName": "NestedInputDemoWorkflow1", + "GlobalParams": [ + { + "Name": "papa", + "Expression": "new object[] { \"1\", 4, null }" + }, + { + "Name": "pa", + "Expression": "papa[0]" + }, + { + "Name": "obj", + "Expression": "new pep[] { new pep(\"popo\" as string, pa as object) }" + } + ], "Rules": [ { "RuleName": "CheckNestedSimpleProp", diff --git a/src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs b/src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs index ac43d10c..9e36cd0d 100644 --- a/src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs +++ b/src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs @@ -37,7 +37,7 @@ private void PopulateMethodInfo() } public Expression Parse(string expression, ParameterExpression[] parameters, Type returnType) { - var config = new ParsingConfig { CustomTypeProvider = new CustomTypeProvider(_reSettings.CustomTypes) }; + var config = new ParsingConfig { CustomTypeProvider = new CustomTypeProvider(_reSettings.CustomTypes), AllowNewToEvaluateAnyType = true }; return new ExpressionParser(parameters, expression, new object[] { }, config).Parse(returnType); } diff --git a/src/RulesEngine/RuleCompiler.cs b/src/RulesEngine/RuleCompiler.cs index 68a153ca..65158878 100644 --- a/src/RulesEngine/RuleCompiler.cs +++ b/src/RulesEngine/RuleCompiler.cs @@ -131,7 +131,7 @@ private RuleExpressionParameter[] GetRuleExpressionParameters(RuleExpressionType catch(Exception ex) { var message = $"{ex.Message}, in ScopedParam: {lp.Name}"; - throw new RuleException(message); + throw new RuleException(message, ex); } } } From 4cb493a0a01a987a1347117a8b3a3ee68082de1e Mon Sep 17 00:00:00 2001 From: Israel Garcia Date: Wed, 2 Oct 2024 03:40:54 -0400 Subject: [PATCH 6/7] include results --- src/RulesEngine/RuleCompiler.cs | 30 ++++++++++++++++-------------- src/RulesEngine/RulesEngine.cs | 4 ++-- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/RulesEngine/RuleCompiler.cs b/src/RulesEngine/RuleCompiler.cs index 65158878..c04086c4 100644 --- a/src/RulesEngine/RuleCompiler.cs +++ b/src/RulesEngine/RuleCompiler.cs @@ -118,25 +118,27 @@ private RuleExpressionParameter[] GetRuleExpressionParameters(RuleExpressionType foreach (var lp in localParams) { - try + if (!ruleExpParams.Any(e => e.ParameterExpression.Name == lp.Name)) { - var lpExpression = expressionBuilder.Parse(lp.Expression, parameters.ToArray(), null); - var ruleExpParam = new RuleExpressionParameter() { - ParameterExpression = Expression.Parameter(lpExpression.Type, lp.Name), - ValueExpression = lpExpression - }; - parameters.Add(ruleExpParam.ParameterExpression); - ruleExpParams.Add(ruleExpParam); - } - catch(Exception ex) - { - var message = $"{ex.Message}, in ScopedParam: {lp.Name}"; - throw new RuleException(message, ex); + try + { + var lpExpression = expressionBuilder.Parse(lp.Expression, parameters.ToArray(), null); + var ruleExpParam = new RuleExpressionParameter() { + ParameterExpression = Expression.Parameter(lpExpression.Type, lp.Name), + ValueExpression = lpExpression + }; + parameters.Add(ruleExpParam.ParameterExpression); + ruleExpParams.Add(ruleExpParam); + } + catch (Exception ex) + { + var message = $"{ex.Message}, in ScopedParam: {lp.Name}"; + throw new RuleException(message, ex); + } } } } return ruleExpParams.ToArray(); - } /// diff --git a/src/RulesEngine/RulesEngine.cs b/src/RulesEngine/RulesEngine.cs index 9a10b53a..de756255 100644 --- a/src/RulesEngine/RulesEngine.cs +++ b/src/RulesEngine/RulesEngine.cs @@ -116,7 +116,7 @@ private async ValueTask ExecuteActionAsync(IEnumerable ruleResul { await ExecuteActionAsync(ruleResult.ChildResults); } - var actionResult = await ExecuteActionForRuleResult(ruleResult, false); + var actionResult = await ExecuteActionForRuleResult(ruleResult, true); ruleResult.ActionResult = actionResult; } } @@ -127,7 +127,7 @@ private async ValueTask ExecuteActionAsync(RuleResultTree ruleResult) { await ExecuteActionAsync(ruleResult.ChildResults); } - var actionResult = await ExecuteActionForRuleResult(ruleResult, false); + var actionResult = await ExecuteActionForRuleResult(ruleResult, true); ruleResult.ActionResult = actionResult; } From 6848ee433963939bb019b531d9ee046cc6a7d01d Mon Sep 17 00:00:00 2001 From: Israel Garcia Date: Wed, 2 Oct 2024 03:57:40 -0400 Subject: [PATCH 7/7] set latest changes with versioning --- src/RulesEngine/HelperFunctions/Utils.cs | 2 +- src/RulesEngine/RulesEngine.csproj | 20 ++++---------------- src/RulesEngine/install-nuget.bat | 1 + src/RulesEngine/nuget.config | 12 ++++++++++++ 4 files changed, 18 insertions(+), 17 deletions(-) create mode 100644 src/RulesEngine/install-nuget.bat diff --git a/src/RulesEngine/HelperFunctions/Utils.cs b/src/RulesEngine/HelperFunctions/Utils.cs index 49f4c515..bfe71fd0 100644 --- a/src/RulesEngine/HelperFunctions/Utils.cs +++ b/src/RulesEngine/HelperFunctions/Utils.cs @@ -13,7 +13,7 @@ namespace RulesEngine.HelperFunctions public static class Utils { /* valid only for netstandard 2.0 */ -#if NETSTANDARD2_0 +#if NETSTANDARD2_0 || NETSTANDARD2_1 public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector) { diff --git a/src/RulesEngine/RulesEngine.csproj b/src/RulesEngine/RulesEngine.csproj index 60a54ad2..c3d253a7 100644 --- a/src/RulesEngine/RulesEngine.csproj +++ b/src/RulesEngine/RulesEngine.csproj @@ -1,8 +1,9 @@ - net6.0;netstandard2.0 - 4.1.0 + netstandard2.0;netstandard2.1 + Library + 4.1.3 Copyright (c) Microsoft Corporation. LICENSE https://github.com/microsoft/RulesEngine @@ -10,20 +11,7 @@ Rules Engine is a package for abstracting business logic/rules/policies out of the system. This works in a very simple way by giving you an ability to put your rules in a store outside the core logic of the system thus ensuring that any change in rules doesn't affect the core system. https://github.com/microsoft/RulesEngine/blob/main/CHANGELOG.md BRE, Rules Engine, Abstraction - - - - true - - - true - true - snupkg - True - ..\..\signing\RulesEngine-publicKey.snk - True - true - true + true diff --git a/src/RulesEngine/install-nuget.bat b/src/RulesEngine/install-nuget.bat new file mode 100644 index 00000000..7ae39ed2 --- /dev/null +++ b/src/RulesEngine/install-nuget.bat @@ -0,0 +1 @@ +dotnet nuget push bin\Debug\RulesEngine.4.1.3.nupkg --interactive --source AvivaLabs -k \ No newline at end of file diff --git a/src/RulesEngine/nuget.config b/src/RulesEngine/nuget.config index 71441d49..fa1c2425 100644 --- a/src/RulesEngine/nuget.config +++ b/src/RulesEngine/nuget.config @@ -2,5 +2,17 @@ + + + + + + + + + + + + \ No newline at end of file