Search code examples
linqexpression-trees

ExpressionTree creation for y => ConstantCollection.Any(x => y.Contains(x)). Different scope parameterExpressions


I'm trying to create an ExpressionTree from one constant string array and one parameter.

var keys = "car,train".Split(','); // "car, train" will be given as a constant.
//var myModel = "MyModel-car"; // this will be given as a parameter
myModel => keys.Any(k => myModel.Contains(k)); // how to create this ExpressionTree? 

I've read through this, but my case is different because the delegate (k => myModel.Contains(k)) has actually two parameters. Here, the k is determined by keys which is a ConstantExpression at runtime. But in the Expression body, it is still a ParameterExpression instance.

I've tried like:

var modelParam = Expression.Parameter(typeof(string)); // Example value: "MyModel-car".
var keys = "car,train".Split(',').AsQueryable().Expression;

var anyMethod = typeof(Enumerable).GetMethods()
                                  .First(x => x.Name == "Any" && x.GetParameters().Length == 1)
                                  .MakeGenericMethod(typeof(string));
var containsMethod = typeof(String).GetMethods()
                                   .Where(x => x.Name == "Contains" && x.GetParameters().Length  == 1)
                                   .First(x => x.GetParameters().First().ParameterType == typeof(string));
var key = Expression.Parameter(typeof(string));
var containsCallExpr = Expression.Call(modelParam, containsMethod, key);
var lambda = Expression.Lambda(containsCallExpr, key);

Expression anyCallExpr = Expression.Call(keys, anyMethod, lambda); // throw an exception
Expression expr = Expression.Lambda(anyCallExpr, modelParam);

However, the anycallExpr line throws an exception:

Static method requires null instance, non-static method requires non-null instance

Does anyone know how to make this work?

[UPDATE]

Based on @Svyatoslav Danyliv's suggestion, I changed the order of argument but a different exception is shown: Incorrect number of arguments supplied for call to method 'Boolean Any[String](System.Collections.Generic.IEnumerable1[System.String])' (Parameter 'method')`

  • I think this exception makes sense because I have two parameters actually.

Solution

  • Try the following realization:

    var modelParam = Expression.Parameter(typeof(string), "model") ; // Example value: "MyModel-car".
    var keys       = "car,train".Split(',').AsEnumerable();
    
    var keysExpression = Expression.Constant(keys);
    
    var containsMethod = typeof(string).GetMethods()
        .Where(x => x.Name == nameof(string.Contains) && x.GetParameters().Length == 1)
        .First(x => x.GetParameters().First().ParameterType == typeof(string));
    
    var key              = Expression.Parameter(typeof(string), "k");
    var containsCallExpr = Expression.Call(modelParam, containsMethod, key);
    var lambda           = Expression.Lambda(containsCallExpr, key);
    
    Expression anyCallExpr = Expression.Call(typeof(Enumerable), nameof(Enumerable.Any),
        new[] { typeof(string) }, keysExpression, lambda);
    
    Expression expr = Expression.Lambda(anyCallExpr, modelParam);