转换 Linq 表达式“obj =>"obj.Prop"进入“父="parent.obj.Prop" [英] Convert Linq expression "obj => obj.Prop" into "parent => parent.obj.Prop"

查看:31
本文介绍了转换 Linq 表达式“obj =>"obj.Prop"进入“父="parent.obj.Prop"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个类型为 Expression> 的现有表达式;它包含像 cust => 这样的值.cust.Name.

I have an existing expression of type Expression<Func<T, object>>; it contains values like cust => cust.Name.

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

I also have a parent class with a field of type T. I need a method that accepts the above as a parameter and generates a new expression that takes the parent class (TModel) as a parameter. This will be used as an expression parameter of an MVC method.

因此,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) 的项目.

The error I'm currently receiving is when I have a field with multiple parts (i.e. cust.Address.State instead of just cust.Name). I get an error on the var member line that the specified member doesn't exist--which is true, since the body at that refers to the parent's child (Customer) and not the item that contains the member (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);
    }

对此的任何帮助将不胜感激.

Any help getting to this point would be greatly appreciated.

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

This was marked as a possible duplicate of this question; however, the only real similarity is the solution (which is, in fact, the same). Composing expressions is not an intuitive solution to accessing nested properties via expressions (unless one's inuition is guided by certain experience, which should not be assumed). I also edited the question to note that the solution needs to be suitable for a paramter of an MVC method, which limits the possible solutions.

推荐答案

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

What you're looking for is the ability to compose expressions, just as you can compose functions:

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

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

This relies on the following method to replace all instances of one expression with another:

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

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

You can now take an expression selecting a property:

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

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

And an expression selecting that object from the model:

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

并组合它们:

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

这篇关于转换 Linq 表达式“obj =>"obj.Prop"进入“父="parent.obj.Prop"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆