如何从属性名称创建 Expression<Func<TModel, string>> 表达式

Posted

技术标签:

【中文标题】如何从属性名称创建 Expression<Func<TModel, string>> 表达式【英文标题】:How to create Expression<Func<TModel, string>> expression from Property Name 【发布时间】:2017-09-28 21:00:25 【问题描述】:

我的 html 辅助方法如下所示

public static MvcHtmlString Control<TModel>(this MyHtmlHelper<TModel> helper, 
    string propertyName, LayoutHelper layout, TemplateType templateType = TemplateType.Screen)

    //...        

我想把我的属性名转换成下面的

 Expression<Func<TModel, string>> expression

任何帮助将不胜感激

【问题讨论】:

您打算如何使用结果表达式? var propertyMetadata = ModelMetadata.FromStringExpression(expression, helper.Html.ViewData); 我需要model.[propertyName]的形式 @Nkosi 希望在给出属性名称时获得表达式 @InTheWorldOfCodingApplications msdn.microsoft.com/en-us/library/mt654263.aspx 【参考方案1】:

您似乎想拨打ModelMetadata.FromLambdaExpression,而不是FromStringExpression。您可以创建一个表达式,如

x => x.PropertyName

从头开始,像这样:

// Get a reference to the property
var propertyInfo = ExpressionHelper.GetPropertyInfo<TModel>(propertyName);
var model = ExpressionHelper.Parameter<TModel>();

// Build the LINQ expression tree backwards:
// x.Prop
var key = ExpressionHelper.GetPropertyExpression(model, propertyInfo);
// x => x.Prop
var keySelector = ExpressionHelper.GetLambda(typeof(TModel), propertyInfo.PropertyType, model, key);

为了使代码更具可读性,将详细的表达式树操作移到此帮助器类中:

public static class ExpressionHelper

    private static readonly MethodInfo LambdaMethod = typeof(Expression)
        .GetMethods()
        .First(x => x.Name == "Lambda" && x.ContainsGenericParameters && x.GetParameters().Length == 2);

    private static MethodInfo GetLambdaFuncBuilder(Type source, Type dest)
    
        var predicateType = typeof(Func<,>).MakeGenericType(source, dest);
        return LambdaMethod.MakeGenericMethod(predicateType);
    

    public static PropertyInfo GetPropertyInfo<T>(string name)
        => typeof(T).GetProperties()
        .Single(p => p.Name == name);

    public static ParameterExpression Parameter<T>()
        => Expression.Parameter(typeof(T));

    public static MemberExpression GetPropertyExpression(ParameterExpression obj, PropertyInfo property)
        => Expression.Property(obj, property);

    public static LambdaExpression GetLambda<TSource, TDest>(ParameterExpression obj, Expression arg)
        => GetLambda(typeof(TSource), typeof(TDest), obj, arg);

    public static LambdaExpression GetLambda(Type source, Type dest, ParameterExpression obj, Expression arg)
    
        var lambdaBuilder = GetLambdaFuncBuilder(source, dest);
        return (LambdaExpression)lambdaBuilder.Invoke(null, new object[]  arg, new[]  obj  );
    

从头开始构建表达式树为您创建 lambda 表达式提供了最大的灵活性。根据目标属性类型,它可能并不总是Expression&lt;Func&lt;TModel, string&gt;&gt; - 最后一个类型可能是int 或其他类型。无论目标属性类型如何,此代码都将构建正确的表达式树。

【讨论】:

这个可以用在ModelMetaData.FromLambdaExpression中吗? @InTheWorldOfCodingApplications 是的,应该可以。如果它没有让我知道。【参考方案2】:

参考以下内容

Creating Expression Trees by Using the API

Expression<Func<TModel, string>> GetPropertyExpression<TModel>(string propertyName) 
    // Manually build the expression tree for 
    // the lambda expression model => model.PropertyName.
    var parameter = Expression.Parameter(typeof(TModel), "model");
    var property = Expression.Property(parameter, propertyName);
    var expression = Expression.Lambda<Func<TModel, string>>(property, parameter);
    return expression;

这将允许您在帮助器中导出表达式

public static MvcHtmlString Control<TModel>(this MyHtmlHelper<TModel> helper, string propertyName,
    LayoutHelper layout, TemplateType templateType = TemplateType.Screen) 
    Expression<Func<TModel, string>> expression = GetPropertyExpression<TModel>(propertyName);
    var propertyMetadata = ModelMetadata.FromStringExpression(expression, helper.Html.ViewData);
    //...other code...

【讨论】:

什么是 var 中的模型参数 = Expression.Parameter(typeof(TModel), "model"); 参数名称。就像你做x =&gt; x.propertyName时一样,参数名是x【参考方案3】:

Expression 只是 lambda 的一个包装器,它创建了一个树形数据结构。像 HTML 助手这样的东西需要这个,所以他们可以内省 lambda 以确定属性名称等东西。类型的内容在Func&lt;TModel, string&gt; 中,这表明它需要一个 lambda,该 lambda 接受某种类型的类实例(泛型)并返回一个字符串(属性值)。换句话说:

m => m.Foo

m 是 lambda 的参数,很可能通过传入您的模型来执行。这里的m 类似于普通方法的类型化参数,因此它可以命名为任何其他可以命名的变量。那么,返回值为Model.Foo,其中Foo 是您正在访问的属性。

【讨论】:

以上是关于如何从属性名称创建 Expression<Func<TModel, string>> 表达式的主要内容,如果未能解决你的问题,请参考以下文章

expression.Compile() 与 ModelMetadata.FromLambdaExpression

如何设置属性选择器的值 Expression<Func<T,TResult>>

spring <aop:pointcut>标签 expression属性如何定义路径

如何从 Expression<Func<MyClass, string>> 动态创建 Expression<Func<MyClass, bool>> 谓

如何动态创建 Expression<Func<MyClass, bool>> 谓词?

使用 lambda 表达式获取属性名称和类型