diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7659711..b9deb2e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
All notable changes to this project will be documented in this file.
+## [2.1.0] - 18-05-2020
+- Adding local param support to make expression authroing more intuitive.
+
## [2.0.0] - 18-05-2020
### Changed
- Interface simplified by removing redundant parameters in the IRulesEngine.
diff --git a/src/RulesEngine/RulesEngine/ExpressionBuilders/LambdaExpressionBuilder.cs b/src/RulesEngine/RulesEngine/ExpressionBuilders/LambdaExpressionBuilder.cs
index ce13397..3a93b78 100644
--- a/src/RulesEngine/RulesEngine/ExpressionBuilders/LambdaExpressionBuilder.cs
+++ b/src/RulesEngine/RulesEngine/ExpressionBuilders/LambdaExpressionBuilder.cs
@@ -38,7 +38,20 @@ namespace RulesEngine.ExpressionBuilders
var binaryExpression = Expression.And(Expression.Constant(true), Expression.Constant(false));
var exceptionMessage = ex.Message;
return Helpers.ToResultTreeExpression(rule, null, binaryExpression, typeParamExpressions, ruleInputExp, exceptionMessage);
- }
+ }
}
+
+ /// Builds the expression for rule parameter.
+ /// The parameter.
+ /// The type parameter expressions.
+ /// The rule input exp.
+ /// Expression.
+ internal override Expression BuildExpressionForRuleParam(LocalParam param, IEnumerable typeParamExpressions, ParameterExpression ruleInputExp)
+ {
+ var config = new ParsingConfig { CustomTypeProvider = new CustomTypeProvider(_reSettings.CustomTypes) };
+ var e = DynamicExpressionParser.ParseLambda(config, typeParamExpressions.ToArray(), null, param.Expression);
+ return e.Body;
+ }
+
}
}
diff --git a/src/RulesEngine/RulesEngine/ExpressionBuilders/RuleExpressionBuilderBase.cs b/src/RulesEngine/RulesEngine/ExpressionBuilders/RuleExpressionBuilderBase.cs
index 0130138..88a6da7 100644
--- a/src/RulesEngine/RulesEngine/ExpressionBuilders/RuleExpressionBuilderBase.cs
+++ b/src/RulesEngine/RulesEngine/ExpressionBuilders/RuleExpressionBuilderBase.cs
@@ -21,5 +21,12 @@ namespace RulesEngine.ExpressionBuilders
/// The rule input exp.
/// Expression type
internal abstract Expression> BuildExpressionForRule(Rule rule, IEnumerable typeParamExpressions, ParameterExpression ruleInputExp);
+
+ /// Builds the expression for rule parameter.
+ /// The rule.
+ /// The type parameter expressions.
+ /// The rule input exp.
+ /// Expression.
+ internal abstract Expression BuildExpressionForRuleParam(LocalParam rule, IEnumerable typeParamExpressions, ParameterExpression ruleInputExp);
}
}
diff --git a/src/RulesEngine/RulesEngine/Models/CompiledParam.cs b/src/RulesEngine/RulesEngine/Models/CompiledParam.cs
new file mode 100644
index 0000000..c05b968
--- /dev/null
+++ b/src/RulesEngine/RulesEngine/Models/CompiledParam.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace RulesEngine.Models
+{
+ ///
+ /// CompiledParam class.
+ ///
+ internal class CompiledParam
+ {
+ ///
+ /// Gets or sets the name.
+ ///
+ ///
+ /// The name.
+ ///
+ internal string Name { get; set; }
+
+ ///
+ /// Gets or sets the value.
+ ///
+ ///
+ /// The value.
+ ///
+ internal Delegate Value { get; set; }
+
+ ///
+ /// Gets or sets the parameters.
+ ///
+ ///
+ /// The parameters.
+ ///
+ internal IEnumerable Parameters { get; set; }
+ }
+}
diff --git a/src/RulesEngine/RulesEngine/Models/CompiledRule.cs b/src/RulesEngine/RulesEngine/Models/CompiledRule.cs
index 6d18a71..f1d7959 100644
--- a/src/RulesEngine/RulesEngine/Models/CompiledRule.cs
+++ b/src/RulesEngine/RulesEngine/Models/CompiledRule.cs
@@ -16,7 +16,16 @@ namespace RulesEngine.Models
///
/// The compiled rules.
///
- internal List CompiledRules { get; set; }
+ internal Delegate Rule { get; set; }
+
+
+ ///
+ /// Gets or sets the rule parameters.
+ ///
+ ///
+ /// The rule parameters.
+ ///
+ internal CompiledRuleParam CompiledParameters { get; set; }
}
}
diff --git a/src/RulesEngine/RulesEngine/Models/CompiledRuleParam.cs b/src/RulesEngine/RulesEngine/Models/CompiledRuleParam.cs
new file mode 100644
index 0000000..cecd2d5
--- /dev/null
+++ b/src/RulesEngine/RulesEngine/Models/CompiledRuleParam.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace RulesEngine.Models
+{
+ /// Class CompiledRule.
+ internal class CompiledRuleParam
+ {
+ ///
+ /// Gets or sets the compiled rules.
+ ///
+ ///
+ /// The compiled rules.
+ ///
+ internal string Name { get; set; }
+
+ /// Gets or sets the rule parameters.
+ /// The rule parameters.
+ internal IEnumerable CompiledParameters { get; set; }
+
+ ///
+ /// Gets or sets the rule parameters.
+ ///
+ ///
+ /// The rule parameters.
+ ///
+ internal IEnumerable RuleParameters { get; set; }
+ }
+}
diff --git a/src/RulesEngine/RulesEngine/Models/LocalParam.cs b/src/RulesEngine/RulesEngine/Models/LocalParam.cs
new file mode 100644
index 0000000..754e0be
--- /dev/null
+++ b/src/RulesEngine/RulesEngine/Models/LocalParam.cs
@@ -0,0 +1,25 @@
+using Newtonsoft.Json;
+
+namespace RulesEngine.Models
+{
+ /// Class Param.
+ /// Implements the
+ public class LocalParam
+ {
+
+ ///
+ /// Gets or sets the name of the rule.
+ ///
+ ///
+ /// The name of the rule.
+ ///
+ [JsonProperty, JsonRequired]
+ public string Name { get; private set; }
+
+ ///
+ /// Gets or Sets the lambda expression.
+ ///
+ [JsonProperty, JsonRequired]
+ public string Expression { get; private set; }
+ }
+}
diff --git a/src/RulesEngine/RulesEngine/Models/Rule.cs b/src/RulesEngine/RulesEngine/Models/Rule.cs
index db1aa9f..f0178fa 100644
--- a/src/RulesEngine/RulesEngine/Models/Rule.cs
+++ b/src/RulesEngine/RulesEngine/Models/Rule.cs
@@ -5,6 +5,7 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+using System.Linq;
namespace RulesEngine.Models
{
@@ -70,12 +71,27 @@ namespace RulesEngine.Models
///
public List Rules { get; set; }
+ ///
+ /// Gets the parameters.
+ ///
+ ///
+ /// The parameters.
+ ///
+ [JsonProperty]
+ public IEnumerable LocalParams { get; private set; }
+
///
/// Gets or Sets the lambda expression.
///
public string Expression { get; set; }
+ ///
+ /// Gets or sets the success event.
+ ///
+ ///
+ /// The success event.
+ ///
public string SuccessEvent { get; set; }
}
diff --git a/src/RulesEngine/RulesEngine/Models/RuleResultTree.cs b/src/RulesEngine/RulesEngine/Models/RuleResultTree.cs
index 2649031..fab28d1 100644
--- a/src/RulesEngine/RulesEngine/Models/RuleResultTree.cs
+++ b/src/RulesEngine/RulesEngine/Models/RuleResultTree.cs
@@ -47,6 +47,14 @@ namespace RulesEngine.Models
///
public string ExceptionMessage { get; set; }
+ ///
+ /// Gets or sets the rule evaluated parameters.
+ ///
+ ///
+ /// The rule evaluated parameters.
+ ///
+ public IEnumerable RuleEvaluatedParams { get; set; }
+
///
/// This method will return all the error and warning messages to caller
///
diff --git a/src/RulesEngine/RulesEngine/Models/WorkflowRules.cs b/src/RulesEngine/RulesEngine/Models/WorkflowRules.cs
index f6db30f..0364719 100644
--- a/src/RulesEngine/RulesEngine/Models/WorkflowRules.cs
+++ b/src/RulesEngine/RulesEngine/Models/WorkflowRules.cs
@@ -17,10 +17,13 @@ namespace RulesEngine.Models
///
public string WorkflowName { get; set; }
- public List WorkflowRulesToInject { get; set; }
+ /// Gets or sets the workflow rules to inject.
+ /// The workflow rules to inject.
+ public IEnumerable WorkflowRulesToInject { get; set; }
+
///
/// list of rules.
///
- public List Rules { get; set; }
+ public IEnumerable Rules { get; set; }
}
}
diff --git a/src/RulesEngine/RulesEngine/ParamCache.cs b/src/RulesEngine/RulesEngine/ParamCache.cs
new file mode 100644
index 0000000..84c7e97
--- /dev/null
+++ b/src/RulesEngine/RulesEngine/ParamCache.cs
@@ -0,0 +1,86 @@
+using RulesEngine.Models;
+using System;
+using System.Collections.Concurrent;
+using System.Linq;
+
+namespace RulesEngine
+{
+ /// Maintains the cache of evaludated param.
+ internal class ParamCache where T : class
+ {
+ ///
+ /// The compile rules
+ ///
+ private readonly ConcurrentDictionary _evaluatedParams = new ConcurrentDictionary();
+
+ ///
+ ///
+ /// Determines whether the specified parameter key name contains parameters.
+ ///
+ ///
+ /// Name of the parameter key.
+ ///
+ /// true if the specified parameter key name contains parameters; otherwise, false.
+ public bool ContainsParams(string paramKeyName)
+ {
+ return _evaluatedParams.ContainsKey(paramKeyName);
+ }
+
+ /// Adds the or update evaluated parameter.
+ /// Name of the parameter key.
+ /// The rule parameters.
+ public void AddOrUpdateParams(string paramKeyName, T ruleParameters)
+ {
+ _evaluatedParams.AddOrUpdate(paramKeyName, ruleParameters, (k, v) => v);
+ }
+
+ /// Clears this instance.
+ public void Clear()
+ {
+ _evaluatedParams.Clear();
+ }
+
+ /// Gets the evaluated parameters.
+ /// Name of the parameter key.
+ /// Delegate[].
+ public T GetParams(string paramKeyName)
+ {
+ return _evaluatedParams[paramKeyName];
+ }
+
+ /// Gets the evaluated parameters cache key.
+ /// Name of the workflow.
+ /// The rule.
+ /// Cache key.
+ public string GetCompiledParamsCacheKey(string workflowName, Rule rule)
+ {
+ if (rule == null)
+ {
+ return string.Empty;
+ }
+ else
+ {
+ if (rule?.LocalParams == null)
+ {
+ return $"Compiled_{workflowName}_{rule.RuleName}";
+ }
+
+ return $"Compiled_{workflowName}_{rule.RuleName}_{string.Join("_", rule?.LocalParams.Select(r => r?.Name))}";
+ }
+ }
+
+ /// Removes the specified workflow name.
+ /// Name of the workflow.
+ public void RemoveCompiledParams(string paramKeyName)
+ {
+ if (_evaluatedParams.TryRemove(paramKeyName, out T ruleParameters))
+ {
+ var compiledKeysToRemove = _evaluatedParams.Keys.Where(key => key.StartsWith(paramKeyName));
+ foreach (var key in compiledKeysToRemove)
+ {
+ _evaluatedParams.TryRemove(key, out T val);
+ }
+ }
+ }
+ }
+}
diff --git a/src/RulesEngine/RulesEngine/ParamCompiler.cs b/src/RulesEngine/RulesEngine/ParamCompiler.cs
new file mode 100644
index 0000000..03e7689
--- /dev/null
+++ b/src/RulesEngine/RulesEngine/ParamCompiler.cs
@@ -0,0 +1,148 @@
+using Microsoft.Extensions.Logging;
+using RulesEngine.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using System.Text;
+using System.Linq;
+
+namespace RulesEngine
+{
+ ///
+ /// Rule param compilers
+ ///
+ internal class ParamCompiler
+ {
+ ///
+ /// The expression builder factory
+ ///
+ private readonly RuleExpressionBuilderFactory _expressionBuilderFactory;
+
+ ///
+ /// The logger
+ ///
+ private readonly ILogger _logger;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The expression builder factory.
+ /// expressionBuilderFactory
+ internal ParamCompiler(RuleExpressionBuilderFactory expressionBuilderFactory, ILogger logger)
+ {
+ if (expressionBuilderFactory == null)
+ {
+ throw new ArgumentNullException($"{nameof(expressionBuilderFactory)} can't be null.");
+ }
+
+ if (logger == null)
+ {
+ throw new ArgumentNullException($"{nameof(logger)} can't be null.");
+ }
+
+ _logger = logger;
+ _expressionBuilderFactory = expressionBuilderFactory;
+ }
+
+ ///
+ /// Compiles the and evaluate parameter expression.
+ ///
+ /// The rule.
+ /// The rule parameters.
+ ///
+ /// IEnumerable<RuleParameter>.
+ ///
+ public CompiledRuleParam CompileParamsExpression(Rule rule, IEnumerable ruleParams)
+ {
+
+ CompiledRuleParam compiledRuleParam = null;
+
+ if (rule.LocalParams != null)
+ {
+ var compiledParameters = new List();
+ var evaluatedParameters = new List();
+ foreach (var param in rule.LocalParams)
+ {
+ IEnumerable typeParameterExpressions = GetParameterExpression(ruleParams.ToArray()).ToList(); // calling ToList to avoid multiple calls this the method for nested rule scenario.
+ ParameterExpression ruleInputExp = Expression.Parameter(typeof(RuleInput), nameof(RuleInput));
+ var ruleParamExpression = GetExpressionForRuleParam(param, typeParameterExpressions, ruleInputExp);
+ var lambdaParameterExps = new List(typeParameterExpressions) { ruleInputExp };
+ var expression = Expression.Lambda(ruleParamExpression, lambdaParameterExps);
+ var compiledParam = expression.Compile();
+ compiledParameters.Add(new CompiledParam { Name = param.Name, Value = compiledParam, Parameters = evaluatedParameters });
+ var evaluatedParam = this.EvaluateCompiledParam(param.Name, compiledParam, ruleParams);
+ ruleParams = ruleParams.Concat(new List { evaluatedParam });
+ evaluatedParameters.Add(evaluatedParam);
+ }
+
+ compiledRuleParam = new CompiledRuleParam { Name = rule.RuleName, CompiledParameters = compiledParameters, RuleParameters = evaluatedParameters };
+ }
+
+ return compiledRuleParam;
+ }
+
+ /// Evaluates the compiled parameter.
+ /// Name of the parameter.
+ /// The compiled parameter.
+ /// The rule parameters.
+ /// RuleParameter.
+ public RuleParameter EvaluateCompiledParam(string paramName, Delegate compiledParam, IEnumerable ruleParams)
+ {
+ var inputs = ruleParams.Select(c => c.Value);
+ var result = compiledParam.DynamicInvoke(new List