Search code examples
c#linqienumerablecontainsexpressionbuilder

How to build a LINQ expression with Contains method from IEnumerable?


I'm trying to build a LINQ expression, to filter values from a int property:

protected IQueryable<T> Some(IEnumerable<int> ids)
{
    var parameter = Expression.Parameter(typeof(T), "x");
    // "Col_id" (int property)
    var property = Expression.Property(parameter, "Col_id");

    MethodInfo method = typeof(Enumerable).
                        GetMethods().
                        Where(x => x.Name == "Contains").
                        Single(x => x.GetParameters().Length == 2).
                        MakeGenericMethod(typeof(T));

    // ids = { 2, 4, 8 } etc...
    var value = Expression.Constant(ids, typeof(IEnumerable<int>));
    var containsMethod = Expression.Call(method, property, value); // exception

    var aDelegate = Expression.Lambda<Func<T, bool>>(containsMethod, parameter);

    table = myDataContext.GetTable<T>();

    return table.AsQueryable().Where(aDelegate);
}

I'm trying to get something like: (x => ids.Contains(x.Col_id)), but an exception is thrown:

Expression of type 'System.Int32' cannot be used for type parameter 'System.Collections.Generic.IEnumerable'1[T] from 'Boolean Contains[T](System.Collections.Generic.IEnumerable'1[T], T)' method


Solution

  • It looks to me like you've just got the arguments the wrong way round.

    This:

    Expression.Call(method, property, value)
    

    means that you're calling:

    Enumerable.Contains(x.Col_id, ids)
    

    whereas you want

    Enumerable.Contains(ids, x.Col_id)
    

    So just try:

    var containsMethod = Expression.Call(method, value, property);
    

    EDIT: Next, you're building the wrong Contains type argument, I think. I suspect you want:

    MethodInfo method = typeof(Enumerable).
                        GetMethods().
                        Where(x => x.Name == "Contains").
                        Single(x => x.GetParameters().Length == 2).
                        MakeGenericMethod(typeof(int));
    

    After all, you want to call Enumerable.Contains<int>, not Enumerable.Contains<SomeType>.