How can I calculate an expression parameter in run

2019-06-11 07:05发布

问题:

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.