Convert List.Contains to Expression Tree

c# expression-trees lambda linq

Question

Related To:

Create a Lambda Expression With 3 conditions

Convert Contains To Expression Tree

In the following of my previous question I faced with this query that I want to write Expression Tree version:

List<byte?> lst = new List<byte?>{1,2};
from a in myTbl
where a.Age = 20 && lst.Contains(a.Status)
select a

I write this code:

List<byte?> lst = new List<byte?>{1,2};
var param = Expression.Parameter(typeof(T), "o");
var body = Expression.AndAlso(
               Expression.Equal(
                    Expression.PropertyOrField(param, "Age"),
                    Expression.Constant(20)
               ),
               Expression.Call(Expression.PropertyOrField(param, "Status"),
                                   "Contains",
                                   Type.EmptyTypes, 
                                   Expression.Constant(lst)));

var lambda = Expression.Lambda<Func<T, bool>>(body, param);
return lambda;

and I get the error:

"No method 'Contains' exists on type 'System.Nullable`1[System.Byte]'."

Please help me to find the problem.

Thanks

Accepted Answer

The problem is that you have switched two arguments to Expression.Call, your code is trying to create the nonsensical expression o.Status.Contains(lst).

You need to switch the two arguments around:

Expression.Call(Expression.Constant(lst),
    "Contains",
    Type.EmptyTypes, 
    Expression.PropertyOrField(param, "Status"))

This is assuming that the LINQ provider you're using understands List<T>.Contains(). If you need Enumerable.Contains(), then have a look at Ivan Stoev's answer.


Popular Answer

The difference from Convert Contains To Expression Tree is that there we were calling a string instance Contains method, while here we need to call a static generic method Enumerable.Contains:

public static bool Contains<TSource>(
    this IEnumerable<TSource> source,
    TSource value
)

It can be achieved by using another Expression.Call overload:

public static MethodCallExpression Call(
    Type type,
    string methodName,
    Type[] typeArguments,
    params Expression[] arguments
)

like this:

// Enumerable.Contains<byte?>(lst, a.Status)
var containsCall = Expression.Call(
    typeof(Enumerable), // type
    "Contains", // method
    new Type[] { typeof(byte?) }, // generic type arguments (TSource)
    Expression.Constant(lst),  // arguments (source)
    Expression.PropertyOrField(param, "Status")  // arguments (value)
);


Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why