将 Linq 表达式“obj => obj.Prop”转换为“parent => parent.obj.Prop”

Posted

技术标签:

【中文标题】将 Linq 表达式“obj => obj.Prop”转换为“parent => parent.obj.Prop”【英文标题】:Convert Linq expression "obj => obj.Prop" into "parent => parent.obj.Prop" 【发布时间】:2016-06-02 21:49:39 【问题描述】:

我有一个Expression<Func<T, object>> 类型的现有表达式;它包含像cust => cust.Name 这样的值。

我还有一个父类,其字段类型为T。我需要一个接受上述作为参数并生成一个以父类 (TModel) 作为参数的新表达式的方法。这将用作 MVC 方法的表达式参数。

因此,cust => cust.Name 变为 parent => parent.Customer.Name

同样,cust => cust.Address.State 变为 parent => parent.Customer.Address.State

这是我的初始版本:

    //note: the FieldDefinition object contains the first expression
    //described above, plus the MemberInfo object for the property/field
    //in question
    public Expression<Func<TModel, object>> ExpressionFromField<TModel>(FieldDefinition<T> field)
        where TModel: BaseModel<T>
    
        var param = Expression.Parameter(typeof(TModel), "t");

        //Note in the next line "nameof(SelectedItem)". This is a reference
        //to the property in TModel that contains the instance from which
        //to retrieve the value. It is unqualified because this method
        //resides within TModel.
        var body = Expression.PropertyOrField(param, nameof(SelectedItem));
        var member = Expression.MakeMemberAccess(body, field.Member);
        return Expression.Lambda<Func<TModel, object>>(member, param);
    

我目前收到的错误是当我有一个包含多个部分的字段时(即cust.Address.State 而不仅仅是cust.Name)。我在var member 行上收到指定成员不存在的错误——这是真的,因为该处的正文指的是父母的孩子(Customer)而不是包含该成员的项目(@987654336 @)。

这是我希望我能做的:

    public Expression<Func<TModel, object>> ExpressionFromField<TModel>(FieldDefinition<T> field)
        where TModel: BaseModel<T>
    
        var param = Expression.Parameter(typeof(TModel), "t");
        var body = Expression.PropertyOrField(param, nameof(SelectedItem));
        var IWantThis = Expression.ApplyExpressionToField(field.Expression, body);
        return Expression.Lambda<Func<TModel, object>>(IWantThis, param);
    

我们将不胜感激任何帮助。

编辑:这被标记为可能与this question 重复;但是,唯一真正的相似之处是解决方案(实际上是相同的)。组合表达式不是通过表达式访问嵌套属性的直观解决方案(除非一个人的直觉是由某些经验指导的,这是不应该假设的)。我还编辑了问题,指出解决方案需要适合 MVC 方法的参数,这限制了可能的解决方案。

【问题讨论】:

为什么不能从父级的子级属性中调用这些表达式,而不是让它们发生变异以直接在父级上工作? 使用parent =&gt; (cust =&gt; cust.Name)(parent.Customer)会简单得多。 @TravisJ,我正在使用所有这些来连接基类,所以很少有细节是预先知道的。可能有更好的方法来做我正在做的事情,但这种方法会奏效。 @poke,我不确定那是什么。您是否将客户视为一种表达方式?你能用未知的表达式做同样的事情吗(我不知道parentcust的类型,或者Name的字段或类型。 How do I compose Linq Expressions? ie Func<Exp<Func<X, Y>>, Exp<Func<Y, Z>>, Exp<Func<X, Z>>>的可能重复 【参考方案1】:

您正在寻找的是编写表达式的能力,就像您可以编写函数一样:

public static Expression<Func<T, TResult>> Compose<T, TIntermediate, TResult>(
    this Expression<Func<T, TIntermediate>> first,
    Expression<Func<TIntermediate, TResult>> second)

    return Expression.Lambda<Func<T, TResult>>(
        second.Body.Replace(second.Parameters[0], first.Body),
        first.Parameters[0]);

这依赖于以下方法将一个表达式的所有实例替换为另一个:

public class ReplaceVisitor:ExpressionVisitor

    private readonly Expression from, to;
    public ReplaceVisitor(Expression from, Expression to)
    
        this.from = from;
        this.to = to;
    

    public override Expression Visit(Expression ex)
    
        if(ex == from) return to;
        else return base.Visit(ex);
      


public static Expression Replace(this Expression ex,
    Expression from,
    Expression to)

    return new ReplaceVisitor(from, to).Visit(ex);

您现在可以使用表达式来选择属性:

Expression<Func<Customer, object>> propertySelector = cust => cust.Name;

以及从模型中选择该对象的表达式:

Expression<Func<CustomerModel, Customer>> modelSelector = model => model.Customer;

并组合它们:

Expression<Func<Customer, object> magic = modelSelector.Compose(propertySelector);

【讨论】:

太酷了。我能够快速实现这一点,但我想花更多时间来弄清楚它是如何工作的。我还对您的答案进行了一些快速编辑;中间有一次拼写错误,还有一次缺少参数类型。 您也可以在访问方法中使用return to

以上是关于将 Linq 表达式“obj => obj.Prop”转换为“parent => parent.obj.Prop”的主要内容,如果未能解决你的问题,请参考以下文章

LinQ 优化

make--变量 条件判断 函数定义及调用

比较接口:Comparable和Comparator

基于其他 int 数组的 C++ 排序

java8 去重

为啥 setter 的 'this._obj['a']=2' 在 Dart 类中不起作用