Guest User

Untitled

a guest
Aug 25th, 2018
108
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.56 KB | None | 0 0
  1. Building a MicroRuleEngine using LinqExpressions
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Linq.Expressions;
  7.  
  8. namespace Trial
  9. {
  10. public class MicroRuleEngine
  11. {
  12. public bool PassesRules<T>(List<Rule> rules, T toInspect)
  13. {
  14. bool pass = true;
  15. foreach (var rule in rules)
  16. {
  17. var cr = this.CompileRule<T>(rule);
  18. pass = pass && cr.Invoke(toInspect);
  19. if (!pass)
  20. return pass;
  21. }
  22. return pass;
  23. }
  24. public Func<T, bool> CompileRule<T>(Rule r)
  25. {
  26. var paramUser = Expression.Parameter(typeof(T));
  27. Expression expr = BuildExpr<T>(r, paramUser);
  28. // build a lambda function User->bool and compile it
  29.  
  30. return Expression.Lambda<Func<T, bool>>(expr, paramUser).Compile();
  31. }
  32.  
  33. Expression BuildExpr<T>(Rule r, ParameterExpression param)
  34. {
  35. Expression propExpression;
  36. Type propType;// typeof(T).GetProperty(r.MemberName).PropertyType;
  37. ExpressionType tBinary;
  38. if (r.MemberName.Contains('.'))
  39. {
  40. // support to be sorted on child fields.
  41. String[] childProperties = r.MemberName.Split('.');
  42. var property = typeof(T).GetProperty(childProperties[0]);
  43. var paramExp = Expression.Parameter(typeof(T), "SomeObject");
  44. propExpression = Expression.MakeMemberAccess(paramExp, property);
  45. for (int i = 1; i < childProperties.Length; i++)
  46. {
  47. property = property.PropertyType.GetProperty(childProperties[i]);
  48. propExpression = Expression.MakeMemberAccess(propExpression, property);
  49. }
  50. propType = propExpression.Type;
  51. propExpression = Expression.Block(new[] { paramExp }, new[]{ propExpression });
  52.  
  53. }
  54. else
  55. {
  56. propExpression = MemberExpression.Property(param, r.MemberName);
  57. propType = propExpression.Type;
  58. }
  59.  
  60. // is the operator a known .NET operator?
  61. if (ExpressionType.TryParse(r.Operator, out tBinary))
  62. {
  63. var right = Expression.Constant(Convert.ChangeType(r.TargetValue, propType));
  64. // use a binary operation, e.g. 'Equal' -> 'u.Age == 15'
  65. return Expression.MakeBinary(tBinary, propExpression, right);
  66. }
  67. else
  68. {
  69. var method = propType.GetMethod(r.Operator);
  70. var tParam = method.GetParameters()[0].ParameterType;
  71. var right = Expression.Constant(Convert.ChangeType(r.TargetValue, tParam));
  72. // use a method call, e.g. 'Contains' -> 'u.Tags.Contains(some_tag)'
  73. return Expression.Call(propExpression, method, right);
  74. }
  75. }
  76.  
  77. }
  78. public class Rule
  79. {
  80. public string MemberName { get; set; }
  81. public string Operator { get; set; }
  82. public string TargetValue { get; set; }
  83. }
  84. }
  85.  
  86. MicroRuleEngine mr = new MicroRuleEngine();
  87. var rules = new List<Rule>() { new Rule() { MemberName = "Shipment.OrderNumber", Operator = "Contains", TargetValue = "55" } };
  88. var pases = mr.PassesRules<Container>(rules, container);
  89. Assert.IsTrue(!pases);
  90. }
  91.  
  92. string expression = "(1 + 2)";
  93. var func = FunctionFactory.Create<int>(expression);
  94.  
  95. int result = func(1, 2); // Result should be 3.
  96.  
  97. expression = "(a * b)";
  98. var func2 = FunctionFactory.Create<int, int, int>(expresion new[] { "a", "b" });
  99.  
  100. int result = func2(10, 50); // Result should be 500;
  101.  
  102. expression = "(Age == 5)";
  103. var func3 = FunctionFactory.Create<Person, bool>(expression);
  104.  
  105. bool isFive = func3(new Person { Age = 5 });
  106.  
  107. public class MicroRuleEngine
  108. {
  109. public bool PassesRules<T>(List<Rule> rules, T toInspect)
  110. {
  111. return this.CompileRules<T>(rules).Invoke(toInspect);
  112. }
  113. public Func<T, bool> CompileRule<T>(Rule r)
  114. {
  115. var paramUser = Expression.Parameter(typeof(T));
  116. Expression expr = BuildExpr<T>(r, paramUser);
  117.  
  118. return Expression.Lambda<Func<T, bool>>(expr, paramUser).Compile();
  119. }
  120.  
  121. public Func<T, bool> CompileRules<T>(IList<Rule> rules)
  122. {
  123. var paramUser = Expression.Parameter(typeof(T));
  124. List<Expression> expressions = new List<Expression>();
  125. foreach (var r in rules)
  126. {
  127. expressions.Add(BuildExpr<T>(r, paramUser));
  128. }
  129. var expr = AndExpressions(expressions);
  130.  
  131. return Expression.Lambda<Func<T, bool>>(expr, paramUser).Compile();
  132. }
  133.  
  134. Expression AndExpressions(IList<Expression> expressions)
  135. {
  136. if(expressions.Count == 1)
  137. return expressions[0];
  138. Expression exp = Expression.And(expressions[0], expressions[1]);
  139. for(int i = 2; expressions.Count > i; i++)
  140. {
  141. exp = Expression.And(exp, expressions[i]);
  142. }
  143. return exp;
  144. }
  145.  
  146. Expression BuildExpr<T>(Rule r, ParameterExpression param)
  147. {
  148. Expression propExpression;
  149. Type propType;
  150. ExpressionType tBinary;
  151. if (r.MemberName.Contains('.'))
  152. {
  153. String[] childProperties = r.MemberName.Split('.');
  154. var property = typeof(T).GetProperty(childProperties[0]);
  155. var paramExp = Expression.Parameter(typeof(T), "SomeObject");
  156.  
  157. propExpression = Expression.PropertyOrField(param, childProperties[0]);
  158. for (int i = 1; i < childProperties.Length; i++)
  159. {
  160. property = property.PropertyType.GetProperty(childProperties[i]);
  161. propExpression = Expression.PropertyOrField(propExpression, childProperties[i]);
  162. }
  163. propType = propExpression.Type;
  164. }
  165. else
  166. {
  167. propExpression = Expression.PropertyOrField(param, r.MemberName);
  168. propType = propExpression.Type;
  169. }
  170.  
  171. // is the operator a known .NET operator?
  172. if (ExpressionType.TryParse(r.Operator, out tBinary))
  173. {
  174. var right = Expression.Constant(Convert.ChangeType(r.TargetValue, propType));
  175. // use a binary operation, e.g. 'Equal' -> 'u.Age == 15'
  176. return Expression.MakeBinary(tBinary, propExpression, right);
  177. }
  178. else
  179. {
  180. var method = propType.GetMethod(r.Operator);
  181. var tParam = method.GetParameters()[0].ParameterType;
  182. var right = Expression.Constant(Convert.ChangeType(r.TargetValue, tParam));
  183. // use a method call, e.g. 'Contains' -> 'u.Tags.Contains(some_tag)'
  184. return Expression.Call(propExpression, method, right);
  185. }
  186. }
  187.  
  188. }
  189. public class Rule
  190. {
  191. public string MemberName { get; set; }
  192. public string Operator { get; set; }
  193. public string TargetValue { get; set; }
  194. }
  195.  
  196. public class Rule
  197. {
  198. public string Description {get; set;}
  199. public Func<T, bool> RuleToApply {get; set;}
  200. }
  201.  
  202. var rules = new List<Rule>() {
  203. new Rule { Description = "OrderNumber Contains 55",
  204. RuleToApply = order => order.OrderNumber.Contains("55") }
  205. };
  206.  
  207. public bool PassesRules<T>(List<Rule> rules, T toInspect)
  208. {
  209. return rules.All(rule => rule(toInspect));
  210. }
Add Comment
Please, Sign In to add comment