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/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 032308e8..0017fb41 100644
--- a/src/RulesEngine/Actions/EvaluateRuleAction.cs
+++ b/src/RulesEngine/Actions/EvaluateRuleAction.cs
@@ -26,10 +26,11 @@ 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/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/HelperFunctions/Utils.cs b/src/RulesEngine/HelperFunctions/Utils.cs
index 4ae9262a..bfe71fd0 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 || NETSTANDARD2_1
+
+ 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..c04086c4 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;
@@ -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);
+ 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();
-
}
///
@@ -250,7 +252,7 @@ private RuleFunc GetWrappedRuleFunc(Rule rule, RuleFunc e.Name);
var result = ruleFunc(extendedInputs.ToArray());
return result;
};
@@ -260,5 +262,7 @@ private RuleExpressionBuilderBase GetExpressionBuilder(RuleExpressionType expres
{
return _expressionBuilderFactory.RuleGetExpressionBuilder(expressionType);
}
+
+
}
}
diff --git a/src/RulesEngine/RulesEngine.cs b/src/RulesEngine/RulesEngine.cs
index 1a7d62e2..de756255 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;
}
@@ -116,14 +116,21 @@ 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
- };
+ var actionResult = await ExecuteActionForRuleResult(ruleResult, true);
+ ruleResult.ActionResult = actionResult;
}
}
+ private async ValueTask ExecuteActionAsync(RuleResultTree ruleResult)
+ {
+ if (ruleResult.ChildResults != null)
+ {
+ await ExecuteActionAsync(ruleResult.ChildResults);
+ }
+ var actionResult = await ExecuteActionForRuleResult(ruleResult, true);
+ 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 7ad5a926..c3d253a7 100644
--- a/src/RulesEngine/RulesEngine.csproj
+++ b/src/RulesEngine/RulesEngine.csproj
@@ -1,8 +1,9 @@
- net6.0;netstandard2.0
- 4.0.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
new file mode 100644
index 00000000..fa1c2425
--- /dev/null
+++ b/src/RulesEngine/nuget.config
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file