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

时间:2016-06-02 21:49:39

标签: c# asp.net-mvc linq linq-expressions

我有一个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)而不是包含该项的项目成员(Address)。

这是我希望我能做到的:

    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方法的参数,这限制了可能的解决方案。

1 个答案:

答案 0 :(得分:11)

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

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);