The problem I need to solve is to run data through IF statements. The IF statements are generated by a SQL table on run time.
I've managed to do this by using expressions and Lambda expressions.
My table has memberName; Operator; Target. So for example I get "Age", "GreaterThan" and "40" from the table, and I compile it.
var rule = new Rule(rowRule["memberName"].ToString(), rowRule["Operator"].ToString(), rowRule["Target"].ToString());
Func<User, bool> compiledRule = CompileRule<User>(rule);
I get a true or false value and it works perfectly.
public static Func<T, bool> CompileRule<T>(Rule r)
{
var paramUser = Expression.Parameter(typeof(User));
Expression expr = BuildExpr<T>(r, paramUser);
// build a lambda function User->bool and compile it
return Expression.Lambda<Func<T, bool>>(expr, paramUser).Compile();
}
static Expression BuildExpr<T>(Rule r, ParameterExpression param)
{
var left = MemberExpression.Property(param, r.MemberName);
var tProp = typeof(T).GetProperty(r.MemberName).PropertyType;
ExpressionType tBinary;
// is the operator a known .NET operator?
if (ExpressionType.TryParse(r.Operator, out tBinary))
{
var right = Expression.Constant(Convert.ChangeType(r.TargetValue, tProp));
// use a binary operation, e.g. 'Equal' -> 'u.Age == 15'
return Expression.MakeBinary(tBinary, left, right);
}
else
{
var method = tProp.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(left, method, right);
}
}
This works great for simple if statements like "Age > 30"
But now I need it more complex, by nesting calculations in the if statements, for example I need to handle statements like "Age > (30 / 2)"
Anyone know how I can adapt my rule engine to be able to do the calculations.
If I can parse the target value I get from the table into code and execute that in a method and return a single value and then create the rule, but I'm not sure how I can get a string in run time, parse into code and execute it.
I went down the rabbit hole of expression trees thinking it could be my answer but I've realized expression trees are just a visual data structure more than code that executes.
If some rule engine like this already exists it would be great but I haven't really seen anything like it.