You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+81-60Lines changed: 81 additions & 60 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,4 +1,4 @@
1
-
NetRuleEngine
1
+
NetRuleEngine
2
2
==============
3
3
C# simple Rule Engine. High performance object rule matching. Support various complex grouped predicates.
4
4
available on [nuget](https://www.nuget.org/packages/NetRuleEngine/).
@@ -36,17 +36,58 @@ flowchart LR
36
36
***Business Event** - an Event that occurs on the business flow, and changes a state on your backend, or a standalone event that needs to be tested for rule matching. can be anything as a site Visit, transaction, UI event, Login, Registration or whatever.
37
37
***Business LOGIC** - this is the backend that reference the NetRuleEngine package, consumes the Rules from DB, and for each Event, run the Rule matching and acts according to the result
38
38
39
-
## Limitations
40
-
- this solution doesn't not provide any rules editor UX or DB.
41
-
- the rules supports up to 2 levels of conditions.
42
-
- supported Scenarios:
43
-
- A OR B
44
-
- A AND B
45
-
- (A AND B AND C AND D AND ...) OR (C AND D)
46
-
- (A OR B) AND (C OR D)
47
-
- (A OR B) AND (C OR D)
48
-
- Not Supported:
49
-
- (A AND OR (C AND D)) OR (X AND Y)
39
+
## Features and Capabilities
40
+
41
+
### Nested Rules Support
42
+
The engine supports unlimited nesting of rule groups, allowing for complex logical expressions. RulesGroups can contain both individual Rules and other RulesGroups, enabling sophisticated rule combinations like:
and many more. See units test for full usage scenarios.
50
91
51
92
#### Simple usage:
52
93
@@ -61,7 +102,7 @@ flowchart LR
61
102
RulesOperator=Rule.InterRuleOperatorType.And,
62
103
RulesGroups=newRulesGroup[] {
63
104
newRulesGroup {
64
-
RulesOperator=Rule.InterRuleOperatorType.And,
105
+
Operator=Rule.InterRuleOperatorType.And,
65
106
// every TestModel instance with NumericField Equal to 5 will match this rule
66
107
Rules=new[] {
67
108
newRule {
@@ -76,6 +117,7 @@ flowchart LR
76
117
});
77
118
```
78
119
120
+
## Technical Details
79
121
- depenent on [LazyCache](https://github.com/alastairtree/LazyCache) to store compiled rules for best performance.
80
122
- compiles [Expression Trees](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/) into dynamic cached code to support high performance usage.
81
123
-**dependency injection** ready, inject Either IRulesService<> or its dependencies.
@@ -87,70 +129,49 @@ Rule Editor UI Example (not included in this project):
87
129
Rule Config JSON Format Example:
88
130
```json
89
131
{
90
-
"Id": "eff37f67-9279-4fff-b2ea-9ef6f92a5de7",
132
+
"Id": "123000000-0000-0000-0000-000000000000",
91
133
"RulesOperator": "And",
92
134
"RulesGroups": [
93
135
{
94
-
"RulesOperator": "Or",
136
+
"Operator": "Or",
95
137
"Rules": [
96
138
{
97
139
"ComparisonPredicate": "TextField",
98
140
"ComparisonOperator": "StringStartsWith",
99
-
"ComparisonValue": "NOT MATCHING PREFIX",
100
-
"PredicateType": null
141
+
"ComparisonValue": "NOT MATCHING PREFIX",
101
142
},
102
143
{
103
-
"ComparisonPredicate": "NumericField",
104
-
"ComparisonOperator": "GreaterThan",
105
-
"ComparisonValue": "4",
106
-
"PredicateType": null
107
-
}
108
-
]
109
-
},
110
-
{
111
-
"RulesOperator": "Or",
112
-
"Rules": [
113
-
{
114
-
"ComparisonPredicate": "TextField",
115
-
"ComparisonOperator": "StringStartsWith",
116
-
"ComparisonValue": "SomePrefix",
117
-
"PredicateType": null
118
-
},
119
-
{
120
-
"ComparisonPredicate": "NumericField",
121
-
"ComparisonOperator": "GreaterThan",
122
-
"ComparisonValue": "55",
123
-
"PredicateType": null
144
+
"Operator": "And",
145
+
"Rules": [
146
+
{
147
+
"ComparisonPredicate": "NumericField",
148
+
"ComparisonOperator": "GreaterThan",
149
+
"ComparisonValue": "10",
150
+
},
151
+
{
152
+
"ComparisonPredicate": "TextField",
153
+
"ComparisonOperator": "Equal",
154
+
"ComparisonValue": "example",
155
+
}
156
+
]
124
157
}
125
158
]
126
159
}
127
160
]
128
161
}
129
162
```
130
-
this example represents a single rule consists on 2 groups with relation of `AND` (which means object must match both groups), on each group, at least 1 rule should match as both have `OR` operator and both have 2 criterias rules.
131
-
132
-
-----------------
133
-
134
-
Features:
135
-
- composite objects
136
-
- enums
137
-
- string
138
-
- numbers
139
-
- datetime
140
-
- Dictionaries
141
-
- collections
142
-
143
-
and many more. See units test for full usage scenarios.
144
-
145
-
146
-
#### decoupling properties names from the rule engine
147
-
best practice would be to decouple the Property names from the way they would be used within the rules (the same concept that JsonPropertyAttribute follows when (de)serializing from/to json). this way, renaming the properties will not break the existing rules.
163
+
This example demonstrates a nested rule structure where:
164
+
- The top level uses an AND operator
165
+
- First group has an OR operator and contains:
166
+
- A simple string matching rule
167
+
- A nested group with an AND operator containing two conditions
168
+
169
+
#### Decoupling properties names from the rule engine
170
+
Best practice would be to decouple the Property names from the way they would be used within the rules (the same concept that JsonPropertyAttribute follows when (de)serializing from/to json). this way, renaming the properties will not break the existing rules.
148
171
use RulePredicatePropertyAttribute to name the rule predicate property, otherwise the property name will be used as predicate name.
149
172
```csharp
150
-
151
-
[RulePredicateProperty("first_name")]
152
-
publicstringFirstName { get; set; }
153
-
173
+
[RulePredicateProperty("first_name")]
174
+
publicstringFirstName { get; set; }
154
175
```
155
176
first_name will be used as predicate name instead of the property name (FirstName), and you will be able to rename the property name (FirstName) without breaking the rules.
0 commit comments