Building Expression Tree for string.Contains [dupl

2019-03-29 10:51发布

问题:

This question already has an answer here:

  • How do I create an expression tree to represent 'String.Contains(“term”)' in C#? 4 answers

I'm struggling to build an expression tree so I can dynamically do filtering on some data.

I have come up with this, but it fails at the var lambda = line

foreach (var rule in request.Where.Rules)
{
    var parameterExpression = Expression.Parameter(typeof(string), rule.Field);
    var left = Expression.Call(parameterExpression, typeof(string).GetMethod("ToLower", Type.EmptyTypes));
    var right = Expression.Constant(rule.Data.ToLower());
    var method = typeof(string).GetMethod("Contains", new [] { typeof(string) });
    var call = Expression.Call(left, method, right);
    var lambda = Expression.Lambda<Func<T, bool>>(call, parameterExpression);
    query = query.Where(lambda);
}

The var rule has a Field (ex "Name") which I want to compare with the text in rule.Data (ex 'tom'). So if T.Name.Contains("tom"); I want the query to include the record, otherwise, not.

The var query is of type IQueryable<T>

EDIT: Finally got it working with this code:

foreach (var rule in request.Where.Rules)
{
    var parameter = Expression.Parameter(typeof(T), "x");
    var property = Expression.Property(parameter, rule.Field);
    var value = Expression.Constant(rule.Data);
    var type = value.Type; 
    var containsmethod = type.GetMethod("Contains", new[] { typeof(string) });
    var call = Expression.Call(property, containsmethod, value);
    var lambda = Expression.Lambda<Func<T, bool>>(call, parameter);
    query = query.Where(lambda);
}

回答1:

You are almost there, but your parameter expression should be of type T, not String, you are also missing the expression that is getting the property from type T like name.

What you should roughly have is this

val -> Expression.Constant(typeof(string), rule.Field)
parameter -> Expression.Parameter(typeof(T), "p")
property -> Expression.Property(parameter, "PropertyName")
contains -> Expression.Call(property, containsmethod, val)
equals true -> Expression.True or equals, something like that

I am freehanding all of that, so it's likely somewhat different to be valid. The resulting expression should be something like this

p => p.Name.Contains(val)


回答2:

If you want to create Where query, you must create lambda then call Where on query and pass lambda. Try this:

Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(call, parameter);
MethodCallExpression expression = Expression.Call(typeof(Queryable), "Where",
                                    new[] { typeof(T) }, query.Expression, lambda);
query = query.Provider.CreateQuery<T>(expression);

instead of

var result = Expression.IsTrue(call);
query = query.Provider.CreateQuery<T>(result);