Skip to content

Commit 93d3f4d

Browse files
committed
Added dictionary support.
1 parent fca435f commit 93d3f4d

File tree

3 files changed

+71
-10
lines changed

3 files changed

+71
-10
lines changed

MicroRuleEngine.Tests/NewAPI.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
34
using Microsoft.VisualStudio.TestTools.UnitTesting;
45
using MicroRuleEngine.Tests.Models;
@@ -177,6 +178,47 @@ public void ListTest()
177178
Assert.IsFalse(passes);
178179
}
179180

181+
class DictTest<T>
182+
{
183+
public Dictionary<T, int> Dict { get; set; }
184+
}
185+
186+
[TestMethod]
187+
public void Dictionary_StringIndex()
188+
{
189+
var objDict = new DictTest<string> { Dict = new Dictionary<string, int>()};
190+
objDict.Dict.Add("Key", 1234);
191+
192+
Rule rule = Rule.Create("Dict['Key']", mreOperator.Equal, 1234);
193+
194+
MRE engine = new MRE();
195+
var compiledRule = engine.CompileRule<DictTest<string>>(rule);
196+
bool passes = compiledRule(objDict);
197+
Assert.IsTrue(passes);
198+
199+
objDict.Dict["Key"] = 2345;
200+
passes = compiledRule(objDict);
201+
Assert.IsFalse(passes);
202+
}
203+
204+
[TestMethod]
205+
public void Dictionary_IntIndex()
206+
{
207+
var objDict = new DictTest<int> { Dict = new Dictionary<int, int>() };
208+
objDict.Dict.Add(111, 1234);
209+
210+
Rule rule = Rule.Create("Dict[111]", mreOperator.Equal, 1234);
211+
212+
MRE engine = new MRE();
213+
var compiledRule = engine.CompileRule<DictTest<int>>(rule);
214+
bool passes = compiledRule(objDict);
215+
Assert.IsTrue(passes);
216+
217+
objDict.Dict[111] = 2345;
218+
passes = compiledRule(objDict);
219+
Assert.IsFalse(passes);
220+
}
221+
180222
[TestMethod]
181223
public void SelfReferenialTest()
182224
{

MicroRuleEngine/MRE.cs

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ protected static Expression BinaryExpression(IEnumerable<Expression> expressions
133133
return expressions.Aggregate(methodExp);
134134
}
135135

136-
static readonly Regex _regexIndexed = new Regex(@"(\w+)\[(\d+)\]", RegexOptions.Compiled);
136+
private static readonly Regex _regexIndexed =
137+
new Regex(@"(?'Collection'\w+)\[(?:(?'Index'\d+)|(?:['""](?'Key'\w+)[""']))\]", RegexOptions.Compiled);
137138

138139
private static Expression GetProperty(Expression param, string propname)
139140
{
@@ -146,26 +147,38 @@ private static Expression GetProperty(Expression param, string propname)
146147
var isIndexed = _regexIndexed.Match(childprop);
147148
if (isIndexed.Success)
148149
{
149-
var collectionname = isIndexed.Groups[1].Value;
150-
var index = Int32.Parse(isIndexed.Groups[2].Value);
150+
var indexType = typeof(int);
151+
var collectionname = isIndexed.Groups["Collection"].Value;
151152
var collectionProp = propertyType.GetProperty(collectionname);
152153
if (collectionProp == null)
153-
throw new RulesException(
154-
$"Cannot find collection property {collectionname} in class {propertyType.Name} (\"{propname}\")");
154+
throw new RulesException(
155+
$"Cannot find collection property {collectionname} in class {propertyType.Name} (\"{propname}\")");
155156
var collexpr = Expression.PropertyOrField(propExpression, collectionname);
156157

158+
Expression expIndex;
159+
if (isIndexed.Groups["Index"].Success)
160+
{
161+
var index = Int32.Parse(isIndexed.Groups["Index"].Value);
162+
expIndex = Expression.Constant(index);
163+
}
164+
else
165+
{
166+
expIndex = Expression.Constant(isIndexed.Groups["Key"].Value);
167+
indexType = typeof(string);
168+
}
169+
157170
var collectionType = collexpr.Type;
158171
if (collectionType.IsArray)
159172
{
160-
propExpression = Expression.ArrayAccess(collexpr, Expression.Constant(index));
173+
propExpression = Expression.ArrayAccess(collexpr, expIndex);
161174
propertyType = propExpression.Type;
162175
}
163176
else
164177
{
165-
var getter = collectionType.GetMethod("get_Item", new Type[] { typeof(Int32) });
178+
var getter = collectionType.GetMethod("get_Item", new Type[] { indexType });
166179
if (getter == null)
167180
throw new RulesException($"'{collectionname} ({collectionType.Name}) cannot be indexed");
168-
propExpression = Expression.Call(collexpr, getter, Expression.Constant(index));
181+
propExpression = Expression.Call(collexpr, getter, expIndex);
169182
propertyType = getter.ReturnType;
170183
}
171184
}
@@ -541,7 +554,7 @@ public static bool IsSimpleType(Type type)
541554
;
542555
}
543556
public static BindingFlags flags = BindingFlags.Instance | BindingFlags.Public;
544-
public static List<Member> GetFields(System.Type type, string memberName = null, string parentPath = null)
557+
public static List<Member> GetFields(Type type, string memberName = null, string parentPath = null)
545558
{
546559
List<Member> toReturn = new List<Member>();
547560
var fi = new Member
@@ -624,7 +637,7 @@ public static bool IsGenericList(Type type)
624637
mreOperator.IsDouble.ToString("g"),
625638
mreOperator.IsDecimal.ToString("g")
626639
};
627-
public static List<Operator> Operators(System.Type type, bool addLogicOperators = false, bool noOverloads = true)
640+
public static List<Operator> Operators(Type type, bool addLogicOperators = false, bool noOverloads = true)
628641
{
629642
List<Operator> operators = new List<Operator>();
630643
if (addLogicOperators)

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ You can reference member properties which are `Arrays` or `List<>` by their inde
5252
Rule rule = Rule.Create("Items[1].Cost", mreOperator.GreaterThanOrEqual, "5.25");
5353
```
5454

55+
Similarly, you can reference element of a string- or integer-keyed dictionary:
56+
```csharp
57+
Rule rule = Rule.Create("Items['myKey'].Cost", mreOperator.GreaterThanOrEqual, "5.25");
58+
```
59+
60+
5561
You can also compare an object to itself indicated by the `*.` at the beginning of the `TargetValue`:
5662
```csharp
5763
Rule rule = Rule.Create("Items[1].Cost", mreOperator.Equal, "*.Items[0].Cost");

0 commit comments

Comments
 (0)