1

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

svick
  • 236,525
  • 50
  • 385
  • 514
Arian
  • 12,793
  • 66
  • 176
  • 300

2 Answers2

3

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)
);
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
1

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.

svick
  • 236,525
  • 50
  • 385
  • 514