From 9bcf4f334b7b98b4ab2d347522ff86b3e884afed Mon Sep 17 00:00:00 2001 From: Abbas Cyclewala Date: Sun, 26 Nov 2023 14:38:11 +0530 Subject: [PATCH] Users/abbasc52/doc updates (#555) * added custom type injection in docs * updated expression parser to support null Resettings and added standalone evaluator sample * fixed missing bracket --- docs/index.md | 88 ++++++++++++++++++- .../RuleExpressionParser.cs | 4 +- test/RulesEngine.UnitTest/TypedClassTests.cs | 22 ++--- 3 files changed, 98 insertions(+), 16 deletions(-) diff --git a/docs/index.md b/docs/index.md index 4ff010d6..8f602120 100644 --- a/docs/index.md +++ b/docs/index.md @@ -9,6 +9,7 @@ RulesEngine is a highly extensible library to build rule based system using C# e - Extending expression via custom class/type injection - Scoped parameters - Post rule execution actions +- Standalone expression evaluator **Table Of Content** - [Installation](#installation) @@ -18,11 +19,13 @@ RulesEngine is a highly extensible library to build rule based system using C# e - [Execute the workflow rules with input:](#execute-the-workflow-rules-with-input) - [Using custom names for inputs](#using-custom-names-for-inputs) - [C# Expression support](#c-expression-support) +- [Extending expression via custom class/type injection](#extending-expression-via-custom-classtype-injection) + - [Example](#example) - [ScopedParams](#scopedparams) - [GlobalParams](#globalparams) - - [Example](#example) - - [LocalParams](#localparams) - [Example](#example-1) + - [LocalParams](#localparams) + - [Example](#example-2) - [Referencing ScopedParams in other ScopedParams](#referencing-scopedparams-in-other-scopedparams) - [Post rule execution actions](#post-rule-execution-actions) - [Inbuilt Actions](#inbuilt-actions) @@ -32,6 +35,8 @@ RulesEngine is a highly extensible library to build rule based system using C# e - [Usage](#usage-1) - [Custom Actions](#custom-actions) - [Steps to use a custom Action](#steps-to-use-a-custom-action) +- [Standalone Expression Evaluator](#standalone-expression-evaluator) + - [Usage](#usage-2) @@ -123,6 +128,59 @@ For more details on supported expression language refer - [expression language]( For supported linq operations refer - [sequence operators](https://dynamic-linq.net/expression-language#sequence-operators) +## Extending expression via custom class/type injection +Although RulesEngine supports C# expressions, you may need to perform more complex operation. + +RulesEngine supports injecting custom classes/types via `ReSettings` which can allow you to call properties and methods of your custom class in expressions + +### Example +Create a custom static class +```c# +using System; +using System.Linq; + +namespace RE.HelperFunctions +{ + public static class Utils + { + public static bool CheckContains(string check, string valList) + { + if (String.IsNullOrEmpty(check) || String.IsNullOrEmpty(valList)) + return false; + + var list = valList.Split(',').ToList(); + return list.Contains(check); + } + } +} +``` + +Add it in your ReSettings and pass in RulesEngine constructor + +```c# + var reSettings = new ReSettings{ + CustomTypes = new Type[] { typeof(Utils) } + } + + var rulesEngine = new RulesEngine.RulesEngine(workflowRules,reSettings); +``` + +With this you can call Utils class in your Rules + +```json +{ + "WorkflowName": "DiscountWithCustomInputNames", + "Rules": [ + { + "RuleName": "GiveDiscount10", + "Expression": "Utils.CheckContains(input1.country, \"india,usa,canada,France\") == true" + } + ] +} + +``` + + ## ScopedParams Sometimes Rules can get very long and complex, scopedParams allow users to replace an expression in rule with an alias making it easier to maintain rule. @@ -471,5 +529,29 @@ Actions can have async code as well } ``` +## Standalone Expression Evaluator +If you are not looking for a full fledged RulesEngine and need only an expression evaluator. RulesEngine offers `RuleExpressionParser` which handles expression parsing and evaluation. + +### Usage +```c# +using System; +using RulesEngine.Models; +using RulesEngine.ExpressionBuilders; + +public class Program +{ + public static void Main() + { + var reParser = new RuleExpressionParser(new ReSettings()); + var result = reParser.Evaluate("a+b", new RuleParameter[]{ + new RuleParameter("a","Hello "), + new RuleParameter("b","World") + }); + Console.WriteLine(result); + } +} +``` +This will output "Hello World" + +For more advanced usage, refer - https://dotnetfiddle.net/KSX8i0 -_For more details please check out [Rules Engine Wiki](https://github.com/microsoft/RulesEngine/wiki)._ diff --git a/src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs b/src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs index 3d169190..9d18dc3f 100644 --- a/src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs +++ b/src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs @@ -19,9 +19,9 @@ public class RuleExpressionParser private readonly ReSettings _reSettings; private readonly IDictionary _methodInfo; - public RuleExpressionParser(ReSettings reSettings) + public RuleExpressionParser(ReSettings reSettings = null) { - _reSettings = reSettings; + _reSettings = reSettings ?? new ReSettings(); _methodInfo = new Dictionary(); PopulateMethodInfo(); } diff --git a/test/RulesEngine.UnitTest/TypedClassTests.cs b/test/RulesEngine.UnitTest/TypedClassTests.cs index 94b39ec5..7b298310 100644 --- a/test/RulesEngine.UnitTest/TypedClassTests.cs +++ b/test/RulesEngine.UnitTest/TypedClassTests.cs @@ -120,11 +120,11 @@ public async Task TypedClassInputSameNameAsTypeTest() var re = new RulesEngine(reSettings); re.AddWorkflow(workflow); - var param = new Transazione { + var param = new Transazione { Attori = new List{ new Attore{ - RuoloAttore = RuoloAttore.B, - + RuoloAttore = RuoloAttore.B, + }, new Attore { RuoloAttore = RuoloAttore.C @@ -137,9 +137,9 @@ public async Task TypedClassInputSameNameAsTypeTest() Assert.All(result, (res) => Assert.True(res.IsSuccess)); - } - - + } + + [Fact] public async Task TypedClassBothAccessibleTestWhenCaseInsensitive() { @@ -163,8 +163,8 @@ public async Task TypedClassBothAccessibleTestWhenCaseInsensitive() Expression = "transazione.Attori != null", }, new() { - RuleName = "Static FieldTest", - Expression = "Transazione.StaticProperty == \"Hello\"" + RuleName = "Static FieldTest", + Expression = "Transazione.StaticProperty == \"Hello\"" } } }; @@ -177,11 +177,11 @@ public async Task TypedClassBothAccessibleTestWhenCaseInsensitive() var re = new RulesEngine(reSettings); re.AddWorkflow(workflow); - var param = new Transazione { + var param = new Transazione { Attori = new List{ new Attore{ - RuoloAttore = RuoloAttore.B, - + RuoloAttore = RuoloAttore.B, + }, new Attore { RuoloAttore = RuoloAttore.C