1

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.
Daebarkee
  • 633
  • 2
  • 6
  • 18
  • Use other overload: `Expression.Call(anyMethod, keys, lambda)` – Svyatoslav Danyliv Jun 07 '22 at 05:59
  • @SvyatoslavDanyliv I've tried your suggestion, but I have another exception as I updated on my original question. – Daebarkee Jun 07 '22 at 08:18
  • My inner lambda (`k => myModel.Contains(k)`) does not have to know about `myModel` parameter because it will be given as a constant when it needs to run. I don't know how to deal with this situation. All other examples are handling exactly one variable. – Daebarkee Jun 07 '22 at 09:34
  • What you are trying to generate at the end? Some extension method? I can build any LINQ expression dynamically but I need to understand why it is needed and maybe there is better solution. – Svyatoslav Danyliv Jun 07 '22 at 09:53
  • I'm trying to create a Condition(Rule)Engine which collect all conditions and produce an aggregated Expression and a DataProvider which returns parameter data holder. One of the existing rule is that, for a given model, it checks if the model name has some special keyword from a given list of keywords. – Daebarkee Jun 07 '22 at 09:58
  • We expect that this ConditionEngine can provide some dynamic rule controls. For example, at runtime, remove certain rules depending on the real-time requirements. – Daebarkee Jun 07 '22 at 10:01

1 Answers1

1

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);
Svyatoslav Danyliv
  • 21,911
  • 3
  • 16
  • 32