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.
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
.