Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Building a MicroRuleEngine using LinqExpressions
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Linq.Expressions;
- namespace Trial
- {
- public class MicroRuleEngine
- {
- public bool PassesRules<T>(List<Rule> rules, T toInspect)
- {
- bool pass = true;
- foreach (var rule in rules)
- {
- var cr = this.CompileRule<T>(rule);
- pass = pass && cr.Invoke(toInspect);
- if (!pass)
- return pass;
- }
- return pass;
- }
- public Func<T, bool> CompileRule<T>(Rule r)
- {
- var paramUser = Expression.Parameter(typeof(T));
- Expression expr = BuildExpr<T>(r, paramUser);
- // build a lambda function User->bool and compile it
- return Expression.Lambda<Func<T, bool>>(expr, paramUser).Compile();
- }
- Expression BuildExpr<T>(Rule r, ParameterExpression param)
- {
- Expression propExpression;
- Type propType;// typeof(T).GetProperty(r.MemberName).PropertyType;
- ExpressionType tBinary;
- if (r.MemberName.Contains('.'))
- {
- // support to be sorted on child fields.
- String[] childProperties = r.MemberName.Split('.');
- var property = typeof(T).GetProperty(childProperties[0]);
- var paramExp = Expression.Parameter(typeof(T), "SomeObject");
- propExpression = Expression.MakeMemberAccess(paramExp, property);
- for (int i = 1; i < childProperties.Length; i++)
- {
- property = property.PropertyType.GetProperty(childProperties[i]);
- propExpression = Expression.MakeMemberAccess(propExpression, property);
- }
- propType = propExpression.Type;
- propExpression = Expression.Block(new[] { paramExp }, new[]{ propExpression });
- }
- else
- {
- propExpression = MemberExpression.Property(param, r.MemberName);
- propType = propExpression.Type;
- }
- // is the operator a known .NET operator?
- if (ExpressionType.TryParse(r.Operator, out tBinary))
- {
- var right = Expression.Constant(Convert.ChangeType(r.TargetValue, propType));
- // use a binary operation, e.g. 'Equal' -> 'u.Age == 15'
- return Expression.MakeBinary(tBinary, propExpression, right);
- }
- else
- {
- var method = propType.GetMethod(r.Operator);
- var tParam = method.GetParameters()[0].ParameterType;
- var right = Expression.Constant(Convert.ChangeType(r.TargetValue, tParam));
- // use a method call, e.g. 'Contains' -> 'u.Tags.Contains(some_tag)'
- return Expression.Call(propExpression, method, right);
- }
- }
- }
- public class Rule
- {
- public string MemberName { get; set; }
- public string Operator { get; set; }
- public string TargetValue { get; set; }
- }
- }
- MicroRuleEngine mr = new MicroRuleEngine();
- var rules = new List<Rule>() { new Rule() { MemberName = "Shipment.OrderNumber", Operator = "Contains", TargetValue = "55" } };
- var pases = mr.PassesRules<Container>(rules, container);
- Assert.IsTrue(!pases);
- }
- string expression = "(1 + 2)";
- var func = FunctionFactory.Create<int>(expression);
- int result = func(1, 2); // Result should be 3.
- expression = "(a * b)";
- var func2 = FunctionFactory.Create<int, int, int>(expresion new[] { "a", "b" });
- int result = func2(10, 50); // Result should be 500;
- expression = "(Age == 5)";
- var func3 = FunctionFactory.Create<Person, bool>(expression);
- bool isFive = func3(new Person { Age = 5 });
- public class MicroRuleEngine
- {
- public bool PassesRules<T>(List<Rule> rules, T toInspect)
- {
- return this.CompileRules<T>(rules).Invoke(toInspect);
- }
- public Func<T, bool> CompileRule<T>(Rule r)
- {
- var paramUser = Expression.Parameter(typeof(T));
- Expression expr = BuildExpr<T>(r, paramUser);
- return Expression.Lambda<Func<T, bool>>(expr, paramUser).Compile();
- }
- public Func<T, bool> CompileRules<T>(IList<Rule> rules)
- {
- var paramUser = Expression.Parameter(typeof(T));
- List<Expression> expressions = new List<Expression>();
- foreach (var r in rules)
- {
- expressions.Add(BuildExpr<T>(r, paramUser));
- }
- var expr = AndExpressions(expressions);
- return Expression.Lambda<Func<T, bool>>(expr, paramUser).Compile();
- }
- Expression AndExpressions(IList<Expression> expressions)
- {
- if(expressions.Count == 1)
- return expressions[0];
- Expression exp = Expression.And(expressions[0], expressions[1]);
- for(int i = 2; expressions.Count > i; i++)
- {
- exp = Expression.And(exp, expressions[i]);
- }
- return exp;
- }
- Expression BuildExpr<T>(Rule r, ParameterExpression param)
- {
- Expression propExpression;
- Type propType;
- ExpressionType tBinary;
- if (r.MemberName.Contains('.'))
- {
- String[] childProperties = r.MemberName.Split('.');
- var property = typeof(T).GetProperty(childProperties[0]);
- var paramExp = Expression.Parameter(typeof(T), "SomeObject");
- propExpression = Expression.PropertyOrField(param, childProperties[0]);
- for (int i = 1; i < childProperties.Length; i++)
- {
- property = property.PropertyType.GetProperty(childProperties[i]);
- propExpression = Expression.PropertyOrField(propExpression, childProperties[i]);
- }
- propType = propExpression.Type;
- }
- else
- {
- propExpression = Expression.PropertyOrField(param, r.MemberName);
- propType = propExpression.Type;
- }
- // is the operator a known .NET operator?
- if (ExpressionType.TryParse(r.Operator, out tBinary))
- {
- var right = Expression.Constant(Convert.ChangeType(r.TargetValue, propType));
- // use a binary operation, e.g. 'Equal' -> 'u.Age == 15'
- return Expression.MakeBinary(tBinary, propExpression, right);
- }
- else
- {
- var method = propType.GetMethod(r.Operator);
- var tParam = method.GetParameters()[0].ParameterType;
- var right = Expression.Constant(Convert.ChangeType(r.TargetValue, tParam));
- // use a method call, e.g. 'Contains' -> 'u.Tags.Contains(some_tag)'
- return Expression.Call(propExpression, method, right);
- }
- }
- }
- public class Rule
- {
- public string MemberName { get; set; }
- public string Operator { get; set; }
- public string TargetValue { get; set; }
- }
- public class Rule
- {
- public string Description {get; set;}
- public Func<T, bool> RuleToApply {get; set;}
- }
- var rules = new List<Rule>() {
- new Rule { Description = "OrderNumber Contains 55",
- RuleToApply = order => order.OrderNumber.Contains("55") }
- };
- public bool PassesRules<T>(List<Rule> rules, T toInspect)
- {
- return rules.All(rule => rule(toInspect));
- }
Add Comment
Please, Sign In to add comment