与 C# 中的嵌套表达式树作斗争

问题描述

我正在尝试在 C# 中创建一个表达式树来动态表示以下嵌套的 lambda...

item => selectorList.Any(selector => terms.Any(sTerm => selector.Contains(sTerm))

地点:

  • 'item' 是一个泛型类型 T
  • 'selectorList' 的类型为 IEnumerable>>
  • 'terms' 是 IEnumerable 类型

经过大量工作,我已经到了这里 - 但现在我正在努力把它做好。我对嵌套时如何使用 ParameterExpression 作为解析值缺少一些基本的了解。

 public static Expression<Func<T,bool>> CreateWhereAnyContainsLambaExpression<T>(IEnumerable<string> terms,params Expression<Func<T,String>>[] selectorList)
    {
        // Create ParameterExpressions
        ParameterExpression qi = Expression.Parameter(typeof(T),"qi");
        ParameterExpression selector = Expression.Parameter(typeof(Expression<Func<T,String>>),"selector");
        ParameterExpression sTerm = Expression.Parameter(typeof(string),"sTerm");

        // Create ConstantExpressions
        ConstantExpression termsConstant = Expression.Constant(terms);
        ConstantExpression selectorListConstant = Expression.Constant(selectorList);

        // Get MethodInfo
        MethodInfo selectorListAny = typeof(Enumerable).getmethods().Where(m => m.Name == "Any").First(m => m.GetParameters().Count() == 2).MakeGenericmethod(typeof(Expression<Func<T,string>>));
        MethodInfo termsAny = typeof(Enumerable).getmethods().Where(m => m.Name == "Any").First(m => m.GetParameters().Count() == 2).MakeGenericmethod(typeof(string));
        MethodInfo selectorContains = typeof(string).getmethod("Contains",new[] { typeof(string) });

        // Build the Expression from inside to out....
        //      selector.Contains(sTerm)
        var expSelectorContains = Expression.Call(selector,selectorContains,sTerm);
        //      sTerm => ...expSelectorContains...
        var sTermlambda = Expression.Lambda<Func<string,bool>>(expSelectorContains,sTerm);
        //      terms.Any(...sTermlambda...)
        var expTermsAny = Expression.Call(termsConstant,termsAny,sTermlambda);
        //      selector => ...expTermsAny...
        var selectorLambda = Expression.Lambda<Func<Expression<Func<T,string>>,bool>>(expTermsAny,selector);
        //      selectorList.Any(...selectorLambda...)
        var expSelectorListAny = Expression.Call(selectorListConstant,selectorListAny,selectorLambda);
        //      item => ...expSelectorListAny...
        var lambda = Expression.Lambda<Func<T,bool>>(expSelectorListAny,qi);
        
        return lambda;
    }

失败就在这条线上

//      selector.Contains(sTerm)
var expSelectorContains = Expression.Call(selector,sTerm);

我在哪里得到异常

System.ArgumentException : Method 'Boolean Contains(System.String)' declared on type 'System.String' cannot be called with instance of type 'System.Linq.Expressions.Expression`1[System.Func`2[TestObject,System.String]]'

鉴于 sTerm 是树更上层的参数表达式,我如何将其解析表示传递给 Contains 方法

解决方法

那么让我澄清一下我造成的困惑... 我正在用 selector.Contains(sTerm)

行构建 lambda var expSelectorContains = Expression.Call(selector,selectorContains,sTerm);

现在应该针对 selectorContains 的实例调用 string MethodInfo。但是我的 selectorExpression<Func<T,String>> 类型的 ParameterExpression。

我只是将它与传入的 selectorList 变量进行了匹配,因为我应该将它与减少时解析的内容进行匹配......这只是一个字符串。

所以修复是改变

ParameterExpression selector = Expression.Parameter(typeof(Expression<Func<T,String>>),"selector");

ParameterExpression selector = Expression.Parameter(typeof(string),"selector");

哦!我的错。真诚感谢您的建议。

,

您正在调用 Enumerable.Any,并尝试传入 Expression<Func<TestObject,string>>Enumerable.Any 采用 Func<TestObject,string>,而不是 Expression<Func<TestObject,string>>

Queryable.Any 但是需要一个 Expression<Func<TestObject,string>>

因此,答案取决于您要尝试做什么。你真的想打电话给Enumerable.Any吗?在这种情况下,您需要将 Expression<Func<TestObject,string>> 编译为实际的 Func<TestObject,string>,然后您可以传递它。或者,由于您似乎完全在表达式中工作(而不是编译它们),Queryable.Any 会更合适吗?