1

I am trying to create an extension method that will be usable for both LINQ-to-Object and LINQ-to-Entities for creating a functioning Where query. More will go into it eventually but to start I am having an issue just getting the method to take a lambda column selection and use it as the base of a Contains() call. I have been able to make something work for LINQ-to-Objects, but when I try to use it for LINQ-to-Entities it has an issue.

Here is what works for LINQ-to-Objects:

public static IQueryable<T> WhereContains<T>(this IQueryable<T> query, Expression<Func<T, string>> column, IList<string> values)
{
    return query.Where(o => values.Contains(column.Compile().Invoke(o)));
}

If this is run against Entity Framework I get an exception stating

LINQ to Entities does not recognize the method...

I have a feeling this is going to require using an ExpressionVisitor, but I haven't been able to figure out how it needs to be wired in. Has anyone been able to accomplish this?

Update:

I would argue that this is not a duplicate being that the answer provided by xanatos showed a straight forward way of accomplishing this without using an ExpressionVisitor.

13
  • You're right that something like that is needed. LINQKit's .AsExpandable() will work here. This has been asked and answered before, let me see if I can find an answer that goes into detail. Commented Jul 16, 2018 at 13:14
  • I think o. shouldn't be there (o => o.values... should be o => values...) Commented Jul 16, 2018 at 13:21
  • You are right on the extra "o.". I was transcribing between systems. Have corrected the example above. Commented Jul 16, 2018 at 13:59
  • @xanatos In what way is the question not a duplicate of the question it was closed as a duplicate of? Commented Jul 16, 2018 at 17:43
  • @Servy The othe question was about the composition of two expressions... This one is only the insertion of one in the other. The only common part seems to be the use of Contains as the method. If you look at the response of the other question, it has to use an ExpressionVisitor. Commented Jul 16, 2018 at 17:45

1 Answer 1

1

Yes, you have to modify a little the Expression, but you don't need an ExpressionVisitor. It is much more simple.

public static IQueryable<TSource> WhereContains<TSource, TResult>(this IQueryable<TSource> query, Expression<Func<TSource, TResult>> column, IList<TResult> values)
{
    MethodInfo iListTResultContains = typeof(ICollection<>).MakeGenericType(typeof(TResult)).GetMethod("Contains", BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(TResult) }, null);

    var contains = Expression.Call(Expression.Constant(values), iListTResultContains, column.Body);

    var lambda = Expression.Lambda<Func<TSource, bool>>(contains, column.Parameters);

    return query.Where(lambda);
}

Note that I've expanded it a little to cover more than string.

Sign up to request clarification or add additional context in comments.

1 Comment

Perfect, thanks. I'm not able to use the generic result due to other constraints with what I'm doing with the data inside of the method but the example you provide nails the syntax. I need to dig into the expression building helpers a lot more.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.