【问题标题】:Struggling with a nested Expression Tree in C#在 C# 中与嵌套表达式树作斗争
【发布时间】:2021-02-03 15:09:01
【问题描述】:

我正在尝试在 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, selectorContains, 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 方法?

【问题讨论】:

  • 在问题中使用“挑战”之类的词的问题在于,根据定义,您不是判断手头任务复杂程度的合适人选,因为您不知道该怎么做它。
  • “复杂”可能会更好,但老实说,我怀疑这与罗斯林家伙必须想出的东西相近。
  • 您的问题的一部分我没有关注:selectorListIEnumerable&lt;Expression&lt;Func&lt;T,string&gt;&gt;&gt;,但随后您在其中一个元素上调用.ContainsFunc&lt;T,string&gt; 没有 .Contains 方法。你能给出这个方法被调用的例外情况,所以我可以看到一些示例输入吗?
  • 我最终被这个问题吞噬了。下面我注意到我将该参数类型切换为 string 以解决您指出的问题 - .Contains 不是针对 'Func' 的有效方法。我想要做的是传入一个选择器(即x=&gt;x.MyProperty) - 实际上它是它们的列表,并且可以将其解析为MyProperty 值是什么对象(它将是string)。然后用它来反对Contains。那有意义吗?我会尝试用更多的上下文来更新我的问题。

标签: c# lambda expression-trees


【解决方案1】:

所以让我澄清一下我造成的混乱...... 我正在使用 var expSelectorContains = Expression.Call(selector, selectorContains, sTerm); 行构造 lambda selector.Contains(sTerm)

现在selectorContains MethodInfo 预计将针对string 的实例调用。但是我的selectorExpression&lt;Func&lt;T, String&gt;&gt; 类型的ParameterExpression。

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

所以解决办法是改变

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

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

哇!我的错。衷心感谢您的建议。

【讨论】:

    【解决方案2】:

    您正在调用Enumerable.Any,并尝试传入Expression&lt;Func&lt;TestObject, string&gt;&gt;Enumerable.Any 采用 Func&lt;TestObject, string&gt;,而不是 Expression&lt;Func&lt;TestObject, string&gt;&gt;

    Queryable.Any 但是确实需要Expression&lt;Func&lt;TestObject, string&gt;&gt;

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

    【讨论】:

    • 感谢您的建议。我已经更新了我的代码以使用 Queryables,因为我认为您是正确的,否则在到达表达式树的 .Any() 部分时它会失败。但是,我无法通过第一个 Expression.Call() 我试图在下面构建加星标的部分:item =&gt; selectorList.Any(selector =&gt; terms.Any(sTerm =&gt; **selector.Contains(sTerm)**) sTerm 是一个 ParameterExpression,但例外是让我知道 string.Contains 需要一个字符串输入.越看越觉得是简单的参数排序失败!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-10
    • 1970-01-01
    • 2015-08-20
    • 2015-03-28
    • 1970-01-01
    • 2016-01-10
    • 1970-01-01
    相关资源
    最近更新 更多