Home > Mobile >  ExpressionTree creation for y => ConstantCollection.Any(x => y.Contains(x)). Different scope p
ExpressionTree creation for y => ConstantCollection.Any(x => y.Contains(x)). Different scope p

Time:06-08

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.

CodePudding user response:

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);
  • Related