【问题标题】:Create LambdaExpression that contain external variable创建包含外部变量的 LambdaExpression
【发布时间】:2013-08-01 10:09:42
【问题描述】:

我想创建类似这样的 lambda

user => user.Address == address

但是没有编译一个,我想返回LambdaExpression

如果 lambda 像这样取常数

user => user.Age == 50

那我可以用这个方法

    public static LambdaExpression PropertyEqual(Type tEntityType, string propertyName, object value)
    {
        // entity => entity.PropName == const
        var itemParameter = Expression.Parameter(tEntityType, "entity");
        return Expression.Lambda
        (
            Expression.Equal
            (
                Expression.Property
                (
                    itemParameter,
                    propertyName
                ),
                Expression.Constant(value) // Tried to replace this with Expression.Parameter or Expression.Variable but no luck
            ),
            new[] { itemParameter }
        );
    }

如何使这个方法接受变量address 来自 lambda 表达式之外的范围?

var addressPropertyName = "Address";
var address = new Address() {...};
var q = Repo.GetQuery().Where(PropertyEqual(typeof(User), addressPropertyName, address))

编辑:澄清我的问题:如何构建正确的Expression 来生成第一个 lambda?

更新:这是不可能的,因为EF does not support non-scalar variable

我按照here 的建议将 lambda 更改为 user => user.AddressId == addressId。这只是如何从已知的导航属性Address 获取AddressId FK PropertyInfo 的问题。

【问题讨论】:

    标签: c# linq entity-framework lambda entity-framework-5


    【解决方案1】:

    您不能在变量上动态生成闭包(您不能将变量的生命周期延长到其上下文之外),因为这是编译器的一个技巧(它会重写您的代码来执行此操作)。

    如果您不想要一个闭包,但您想要一个额外的参数,那么您可以在表达式中添加一个额外的参数。

    你可以

    Expression<Func<string>> myExpr = () => address;
    

    现在您有一个围绕您的地址结束的表达式。现在您只需将这两个表达式结合起来即可。

    您必须将方法更改为:

    public static LambdaExpression PropertyEqual<T>(Type tEntityType, string propertyName, Expression<Func<T>> getValue)
    {
       // entity => entity.PropName == const
       var itemParameter = Expression.Parameter(tEntityType, "entity");
       return Expression.Lambda
       (
           Expression.Equal
           (
               Expression.Property
               (
                   itemParameter,
                   propertyName
               ),
               Expression.Invoke(getValue) // You could directly use getValue.Body instead of Expression.Invoke(getValue)
           ),
           new[] { itemParameter }
       );
    }
    

    【讨论】:

    • 添加额外的参数会变成类似(user, address) =&gt; user.Address == address而不是user =&gt; user.Address == address,它有不同的返回表达式Expression&lt;Func&lt;User, Address, bool&gt;&gt;而不是Expression&lt;Func&lt;User, bool&gt;&gt;
    • 您无法获得对变量的“引用”并将其放入ExpressionTypedReference 和表达式树不是“兼容的”。您可以做的是预先创建一个指向您的地址的表达式,然后组合两个表达式树。闭包将由编译器完成。
    • 对不起,如何组合两个表达式并返回LambdaExpressionExpression.Lambda(...) 只接受一个 Expression 正文。
    • 我得到“Invoke 在 LINQ to Entities 中不受支持”并且如果使用 getValue.Body 我得到错误“无法创建常量值...”。我认为是因为 address 变量是 EF 实体而不是值类型。
    • @CallMeLaNN 那么是的,我认为你的问题是你不能直接这样做(正如你提供的链接所说)。你可以比较他们的.ID
    猜你喜欢
    • 1970-01-01
    • 2011-03-10
    • 2014-04-27
    • 1970-01-01
    • 2014-10-17
    • 1970-01-01
    • 2015-01-29
    • 2012-03-28
    • 2013-08-08
    相关资源
    最近更新 更多