diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 6cfe968b..af5df500 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -44,6 +44,7 @@ jobs:
6.0.x
8.0.x
9.0.x
+ 10.0.x
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
diff --git a/.github/workflows/dotnetcore-build.yml b/.github/workflows/dotnetcore-build.yml
index 2e775a36..b35626b2 100644
--- a/.github/workflows/dotnetcore-build.yml
+++ b/.github/workflows/dotnetcore-build.yml
@@ -18,6 +18,7 @@ jobs:
6.0.x
8.0.x
9.0.x
+ 10.0.x
- name: Install dependencies
run: dotnet restore RulesEngine.sln
diff --git a/benchmark/RulesEngineBenchmark/RulesEngineBenchmark.csproj b/benchmark/RulesEngineBenchmark/RulesEngineBenchmark.csproj
index 0eb2c7bf..fc9e552b 100644
--- a/benchmark/RulesEngineBenchmark/RulesEngineBenchmark.csproj
+++ b/benchmark/RulesEngineBenchmark/RulesEngineBenchmark.csproj
@@ -2,7 +2,7 @@
Exe
- net6.0;net8.0;net9.0
+ net6.0;net8.0;net9.0;net10.0
diff --git a/demo/DemoApp.EFDataExample/DemoApp.EFDataExample.csproj b/demo/DemoApp.EFDataExample/DemoApp.EFDataExample.csproj
index 40577574..03ce0f47 100644
--- a/demo/DemoApp.EFDataExample/DemoApp.EFDataExample.csproj
+++ b/demo/DemoApp.EFDataExample/DemoApp.EFDataExample.csproj
@@ -1,7 +1,7 @@
- net8.0;net9.0
+ net8.0;net9.0;net10.0
DemoApp.EFDataExample
DemoApp.EFDataExample
diff --git a/demo/DemoApp/DemoApp.csproj b/demo/DemoApp/DemoApp.csproj
index 83f0d8a7..bb37d79a 100644
--- a/demo/DemoApp/DemoApp.csproj
+++ b/demo/DemoApp/DemoApp.csproj
@@ -2,7 +2,7 @@
Exe
- net8.0;net9.0
+ net8.0;net9.0;net10.0
DemoApp.Program
diff --git a/global.json b/global.json
index a1481352..010597ab 100644
--- a/global.json
+++ b/global.json
@@ -1,7 +1,7 @@
{
"sdk": {
- "version": "9.0.301",
+ "version": "10.0.100",
"rollForward": "latestFeature",
- "allowPrerelease": false
+ "allowPrerelease": true
}
}
\ No newline at end of file
diff --git a/src/RulesEngine/HelperFunctions/Utils.cs b/src/RulesEngine/HelperFunctions/Utils.cs
index 05df93dd..98c0953a 100644
--- a/src/RulesEngine/HelperFunctions/Utils.cs
+++ b/src/RulesEngine/HelperFunctions/Utils.cs
@@ -19,6 +19,11 @@ public static object GetTypedObject(dynamic input)
Type type = CreateAbstractClassType(input);
return CreateObject(type, input);
}
+ else if (input is IDictionary dict)
+ {
+ Type type = CreateAbstractClassTypeFromDictionary(dict);
+ return CreateObjectFromDictionary(type, dict);
+ }
else
{
return input;
@@ -129,6 +134,89 @@ private static IList ToList(this IEnumerable self, Type innerType)
var genericMethod = methodInfo.MakeGenericMethod(innerType);
return genericMethod.Invoke(null, new[] { self }) as IList;
}
+
+ private static Type CreateAbstractClassTypeFromDictionary(IDictionary dictionary)
+ {
+ List props = [];
+
+ foreach (var kvp in dictionary)
+ {
+ Type valueType;
+ if (kvp.Value is ExpandoObject)
+ {
+ valueType = CreateAbstractClassType(kvp.Value);
+ }
+ else if (kvp.Value is IDictionary nestedDict)
+ {
+ valueType = CreateAbstractClassTypeFromDictionary(nestedDict);
+ }
+ else if (kvp.Value is IList list)
+ {
+ if (list.Count == 0)
+ {
+ valueType = typeof(List
-
+
@@ -53,7 +53,7 @@
-
+
diff --git a/test/RulesEngine.UnitTest/RuleExpressionParserTests/RuleExpressionParserTests.cs b/test/RulesEngine.UnitTest/RuleExpressionParserTests/RuleExpressionParserTests.cs
index aa0ffb4e..0be1fe93 100644
--- a/test/RulesEngine.UnitTest/RuleExpressionParserTests/RuleExpressionParserTests.cs
+++ b/test/RulesEngine.UnitTest/RuleExpressionParserTests/RuleExpressionParserTests.cs
@@ -69,6 +69,42 @@ public void TestExpressionWithDifferentCompilerSettings(bool fastExpressionEnabl
var result = ruleParser.Evaluate("d1 < 20", new[] { Models.RuleParameter.Create("d1", d1) });
Assert.False(result);
}
+
+ [Fact]
+ public void TestExpressionWithDictionaryParameter()
+ {
+ var parser = new RuleExpressionParser(new ReSettings());
+
+ var payload = new System.Collections.Generic.Dictionary
+ {
+ { "Formule", "Essentielle" }
+ };
+
+ var ruleParameters = new[] { RuleParameter.Create("_", payload) };
+
+ var resultNotEqual = parser.Evaluate("Formule != \"Essentielle\"", ruleParameters);
+ Assert.False(resultNotEqual);
+
+ var resultEqual = parser.Evaluate("Formule == \"Essentielle\"", ruleParameters);
+ Assert.True(resultEqual);
+ }
+
+ [Fact]
+ public void TestExpressionWithDictionaryParameter_MultipleKeys()
+ {
+ var parser = new RuleExpressionParser(new ReSettings());
+
+ var payload = new System.Collections.Generic.Dictionary
+ {
+ { "Name", "John" },
+ { "Age", 30 }
+ };
+
+ var ruleParameters = new[] { RuleParameter.Create("input", payload) };
+
+ var result = parser.Evaluate("Name == \"John\" && Age == 30", ruleParameters);
+ Assert.True(result);
+ }
}
diff --git a/test/RulesEngine.UnitTest/RulesEngine.UnitTest.csproj b/test/RulesEngine.UnitTest/RulesEngine.UnitTest.csproj
index 281ea8a6..f33bcd57 100644
--- a/test/RulesEngine.UnitTest/RulesEngine.UnitTest.csproj
+++ b/test/RulesEngine.UnitTest/RulesEngine.UnitTest.csproj
@@ -1,6 +1,6 @@
- net6.0;net8.0;net9.0
+ net6.0;net8.0;net9.0;net10.0
True
..\..\signing\RulesEngine-publicKey.snk
True
diff --git a/test/RulesEngine.UnitTest/UtilsTests.cs b/test/RulesEngine.UnitTest/UtilsTests.cs
index ef88ec4d..3dd21044 100644
--- a/test/RulesEngine.UnitTest/UtilsTests.cs
+++ b/test/RulesEngine.UnitTest/UtilsTests.cs
@@ -103,6 +103,120 @@ public void CreateAbstractType_dynamicObject()
}
+ [Fact]
+ public void GetTypedObject_Dictionary_ReturnsTypedObject()
+ {
+ var dict = new Dictionary
+ {
+ { "Name", "Alice" },
+ { "Age", 25 }
+ };
+
+ var result = Utils.GetTypedObject(dict);
+ Assert.IsNotType>(result);
+ Assert.NotNull(result.GetType().GetProperty("Name"));
+ Assert.NotNull(result.GetType().GetProperty("Age"));
+ }
+
+ [Fact]
+ public void GetTypedObject_Dictionary_NestedDictionary()
+ {
+ var dict = new Dictionary
+ {
+ { "Name", "Alice" },
+ { "Address", new Dictionary
+ {
+ { "City", "Seattle" },
+ { "Zip", "98101" }
+ }
+ }
+ };
+
+ var result = Utils.GetTypedObject(dict);
+ Assert.IsNotType>(result);
+ var addressProp = result.GetType().GetProperty("Address");
+ Assert.NotNull(addressProp);
+ var address = addressProp.GetValue(result);
+ Assert.NotNull(address.GetType().GetProperty("City"));
+ Assert.NotNull(address.GetType().GetProperty("Zip"));
+ }
+
+ [Fact]
+ public void GetTypedObject_Dictionary_WithList()
+ {
+ var dict = new Dictionary
+ {
+ { "Name", "Alice" },
+ { "Scores", new List { 90, 85, 92 } }
+ };
+
+ var result = Utils.GetTypedObject(dict);
+ Assert.IsNotType>(result);
+ Assert.NotNull(result.GetType().GetProperty("Name"));
+ Assert.NotNull(result.GetType().GetProperty("Scores"));
+ }
+
+ [Fact]
+ public void GetTypedObject_Dictionary_WithEmptyList()
+ {
+ var dict = new Dictionary
+ {
+ { "Items", new List() }
+ };
+
+ var result = Utils.GetTypedObject(dict);
+ Assert.IsNotType>(result);
+ Assert.NotNull(result.GetType().GetProperty("Items"));
+ }
+
+ [Fact]
+ public void GetTypedObject_Dictionary_WithNestedExpandoObject()
+ {
+ dynamic nested = new ExpandoObject();
+ nested.Value = "test";
+
+ var dict = new Dictionary
+ {
+ { "Nested", (object)nested }
+ };
+
+ var result = Utils.GetTypedObject(dict);
+ Assert.IsNotType>(result);
+ var nestedProp = result.GetType().GetProperty("Nested");
+ Assert.NotNull(nestedProp);
+ }
+
+ [Fact]
+ public void GetTypedObject_Dictionary_WithListOfDictionaries()
+ {
+ var dict = new Dictionary
+ {
+ { "People", new List
+ {
+ new Dictionary { { "Name", "Alice" } },
+ new Dictionary { { "Name", "Bob" } }
+ }
+ }
+ };
+
+ var result = Utils.GetTypedObject(dict);
+ Assert.IsNotType>(result);
+ Assert.NotNull(result.GetType().GetProperty("People"));
+ }
+
+ [Fact]
+ public void GetTypedObject_Dictionary_WithNullValue()
+ {
+ var dict = new Dictionary
+ {
+ { "Name", "Alice" },
+ { "Middle", null }
+ };
+
+ var result = Utils.GetTypedObject(dict);
+ Assert.IsNotType>(result);
+ Assert.NotNull(result.GetType().GetProperty("Name"));
+ }
}
}