rism
rism

Reputation: 12142

Convert lambda with any | contains to expression tree

How can the following simple lambda be represented using Expression Tree syntax?

v.Tags.Any(t => searchTags.Contains(t.ID));

Where :

Variations on the following have been tried:

 var anyInfo = typeof(Queryable).GetMethods()
         .Where(m => m.Name == "Any")
         .Single(m => m.GetParameters().Length == 2)
         .MakeGenericMethod(typeof(Tag));


 var toQueryable = typeof(Queryable).GetMethods()
         .Where(m => m.Name == "AsQueryable")
         .Single(m => m.IsGenericMethod)
         .MakeGenericMethod(typeof(Tag));

 var containsInfo = typeof(List<long>).GetMethod("Contains", new Type[] { typeof(long) });

 var list = Expression.Constant(searchTags);
 var mcvalue = Expression.Property(v, "Tags");
 ParameterExpression tagParam = Expression.Parameter(typeof(Tag), "mt");

 var tagID = Expression.Property(tagParam, "ID");
 var st = Expression.Call(list, containsInfo, tagID);
 return Expression.Call(null, anyInfo, Expression.Call(null, toQueryable, mcvalue), st);

Upvotes: 1

Views: 462

Answers (1)

Ivan Stoev
Ivan Stoev

Reputation: 205649

Here is the C# equivalent of the result of your return statement:

v.Tags.AsQueryable().Any(searchTags.Contains(t.ID))

Aside from the redundant AsQueryable() call, the main issue is that neither the result, nor the Any call argument are lambda expressions, but expressions representing a potential lambda expression body. In order to convert them to lambda expressions (and connect with the associated parameter expressions), you need to use one of the Expression.Lambda method overloads.

So building lambda expression like

v => v.Tags.Any(t => searchTags.Contains(t.ID));

could be done with something like this:

static Expression<Func<T, bool>> SampleLambda<T>(List<long> searchTags)
{
    var v = Expression.Parameter(typeof(T), "v");

    var t = Expression.Parameter(typeof(Tag), "t");

    var containsCall = Expression.Call(
        typeof(Enumerable), "Contains", new [] { typeof(long) },
        Expression.Constant(searchTags),
        Expression.Property(t, "ID")
    );

    var anyCall = Expression.Call(
        typeof(Enumerable), "Any", new [] { typeof(Tag) },
        Expression.Property(v, "Tags"),
        Expression.Lambda(containsCall, t)
    );

    return Expression.Lambda<Func<T, bool>>(anyCall, v);
}

Upvotes: 2

Related Questions