Generating Expression Tree for List.Any(v => v.Contains(Book.Title.ToString()))

c# expression expression-trees lambda

Question

As the title says, I'm trying to generate an Expression tree for cheking if any item in a list of strings matches a string of a Book object.

So far I've got this:

private static Expression<Func<Books, bool>> GenerateListContainsLikeExpression(string propertyName, List<string> values)
    {
        var parameter = Expression.Parameter(typeof(Books), "b");
        var listParameter = Expression.Parameter(typeof(string), "v");
        var property = Expression.Property(parameter, propertyName);
        var anyMethod = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public).First(m => m.Name == "Any" && m.GetParameters().Count() == 2).MakeGenericMethod(typeof(string));
        var toStringMethod = typeof(object).GetMethod("ToString");
        var containsMethod = typeof(string).GetMethod("Contains");
        var objectString = Expression.Call(property, toStringMethod);
        var lambda = Expression.Call(listParameter, containsMethod, objectString);
        var func = Expression.Lambda<Func<List<string>, bool>>(lambda, parameter);
        var comparison = Expression.Call(anyMethod, Expression.Constant(values), func);

        return Expression.Lambda<Func<Books, bool>>(comparison, parameter);
    }

But I'm getting this error:

Expression of type 'System.Boolean' cannot be used for parameter of type 'System.Func2[System.String,System.Boolean]' of method 'Boolean Any[String](System.Collections.Generic.IEnumerable1[System.String], System.Func`2[System.String,System.Boolean])'

At this line:

var comparison = Expression.Call(anyMethod, Expression.Constant(values), lambda);

I feel like I just need the last little bit.

Thanks in advance :-)

EDIT: For clarification. I'll explain a little more detailed what I needed.

I needed to categorize books based on some of the books properties. In this specific case, I pass a list of strings to the function. I needed to check if any of these strings was contained in any books title.

So first of all, the lambda I thought I needed was wrong to begin with. This is the lambda that I actually needed: b => values.Any(v => b.Title.ToString().Contains(v))

And here is the final code I ended up with:

private static Expression<Func<Books, bool>> GenerateListContainsLikeExpression(string propertyName, List<string> values)
    {
        var parameter = Expression.Parameter(typeof(Books), "b");
        var listParameter = Expression.Parameter(typeof(string), "v");
        var property = Expression.Property(parameter, propertyName);
        var anyMethod = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public).First(m => m.Name == "Any" && m.GetParameters().Count() == 2).MakeGenericMethod(typeof(string));
        var toStringMethod = typeof(object).GetMethod("ToString");
        var containsMethod = typeof(string).GetMethod("Contains");
        var objectString = Expression.Call(property, toStringMethod);
        var lambda = Expression.Call(objectString, containsMethod, listParameter);
        var func = Expression.Lambda<Func<string, bool>>(lambda, listParameter);
        var comparison = Expression.Call(anyMethod, Expression.Constant(values), func);

        return Expression.Lambda<Func<Books, bool>>(comparison, parameter);
    }

Accepted Answer

Final working code:

private static Expression<Func<Books, bool>> GenerateListContainsLikeExpression(string propertyName, List<string> values)
{
    var parameter = Expression.Parameter(typeof(Books), "b");
    var listParameter = Expression.Parameter(typeof(string), "v");
    var property = Expression.Property(parameter, propertyName);
    var anyMethod = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public).First(m => m.Name == "Any" && m.GetParameters().Count() == 2).MakeGenericMethod(typeof(string));
    var toStringMethod = typeof(object).GetMethod("ToString");
    var containsMethod = typeof(string).GetMethod("Contains");
    var objectString = Expression.Call(property, toStringMethod);
    var lambda = Expression.Call(objectString, containsMethod, listParameter);
    var func = Expression.Lambda<Func<string, bool>>(lambda, listParameter);
    var comparison = Expression.Call(anyMethod, Expression.Constant(values), func);

    return Expression.Lambda<Func<Books, bool>>(comparison, parameter);
}

For a more in depth explanation of the problem. See my edit in the original post.



Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow