【问题标题】:Need help converting "Any()" lambda to an expression tree需要帮助将“Any()”lambda 转换为表达式树
【发布时间】:2017-09-24 17:08:13
【问题描述】:

考虑以下问题。 (可行,但我需要将 Accounts 作为字符串传入。Accounts 是 Account 列表)

repo = repo.Where(x => x.Accounts.Any(p => p.Id == 1));

这是我目前所拥有的,但最后我似乎无法将它们连接在一起。

        var parameterExp = Expression.Parameter(typeof(Car), "x");
        Expression propertyExp = Expression.Property(parameterExp, "Accounts");

        //Type elementType = propertyExp.Type.GetGenericArguments()[0];

        MethodInfo AnyMethod = typeof(Enumerable).GetMethods(BindingFlags.Public | BindingFlags.Static).First(m => m.Name == "Any");

        var parameterExp2 = Expression.Parameter(typeof(Car), "p");

        var idExpr = Expression.PropertyOrField(parameterExp2, "Id");

        MethodInfo method = typeof(long).GetMethod("Equals", new[] { typeof(long) });
        var _relatedToId = Expression.Constant(relatedToId, typeof(long));
        var equalsMethodExp = Expression.Call(idExpr, method, _relatedToId);

        // This is where it breaks. I can't seem to wire it together correctly.
        var call = Expression.Call(
            AnyMethod,
            propertyExp,
            equalsMethodExp);

        Expression<Func<Car, bool>> predicate = Expression.Lambda<Func<Car, bool>>(call, parameterExp);

        // Need to return x => x.Accounts.Any(p => p.Id.Equals(1))
        return predicate;

提前致谢

【问题讨论】:

  • 请解释xp的类型
  • x 是 Car 类型,p 是 Account 类型,其中“Accounts”是 Account 列表。即 MyCar.Accounts

标签: c# linq lambda tree expression


【解决方案1】:

你有几个错误:

  • 首先,取消下面的注释

    Type elementType = propertyExp.Type.GetGenericArguments()[0];
    
  • 其次,您没有正确检索MethodInfoAny。您需要包含两个参数(source、func)的信息,并且您需要获取 Any Any&lt;Account&gt; 的封闭类型而不是开放 Any&lt;&gt;

    MethodInfo AnyMethod = typeof(Enumerable)
                           .GetMethods(BindingFlags.Public | BindingFlags.Static)
                           .First(m => m.Name == "Any" && m.GetParameters().Length > 1);
    var closedAnyMethod = AnyMethod.MakeGenericMethod(elementType);
    
  • 第三,parameterExp2 的类型不正确。应该是Account 而不是Car

    var parameterExp2 = Expression.Parameter(elementType, "p");
    
  • 第四,您应该将MethodCallExpression for Equals 方法转换为lambda 表达式。您不能直接将 Equals' 调用表达式传递给 Any,因为 AnyFunc&lt;T, bool&gt; 作为参数:

    var equalsMethodExp = Expression.Call(idExpr, method, _relatedToId);
    var lambdaAny = Expression.Lambda(typeof(Func<,>).MakeGenericType(elementType, typeof(bool)), equalsMethodExp, parameterExp2);
    
  • 最后,你只需要调用Expression.Call的另一个覆盖:

    var call = Expression.Call(
            closedAnyMethod,
            propertyExp,
            lambdaAny);
    

如果你想获得完整的代码,请告诉我。

【讨论】:

  • 这真的很接近。帐户作为字符串传入。 Car 是运行时唯一已知的类型。我一直从 Type elementType = propertyExp.Type.GetGenericArguments()[0];这给出了类型帐户。一切正常,直到 lambdaAny var lambdaAny = Expression.Lambda>(equalsMethodExp, parameterExp2); typeof(elementType) 似乎不满足 Func.
  • 您能否提供更多信息var lambdaAny = Expression.Lambda&lt;Func&lt;Account, bool&gt;&gt;(equalsMethodExp, parameterExp2); 有什么问题?它是编译或运行时错误,还是无法正常工作?你能在调试中发布equalsMethodExp 的表示方式吗?是不是看起来像这样:{p.Id.Equals(1)}
  • 您的示例中的帐户是您拥有的具体类型。我现在不知道 Account 在编译时会是什么。类型会改变。我需要像 Expression.Lambda>(equalsMethodExp, parameterExp2); 这样的东西除非我在 Func 中有一个具体的类型,否则它不会构建。 equalsMethodExp 的值正确
  • @th3monk3y 请检查答案,我编辑了它。现在在运行时从propertyExp 检索类型。如果你愿意,你可以从某个地方传递它。
  • 有效!!非常感谢乔治!
猜你喜欢
  • 1970-01-01
  • 2012-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-27
  • 1970-01-01
相关资源
最近更新 更多