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

5

我的Html帮助方法如下所示

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

我希望将我的属性名称转换为以下格式:
 Expression<Func<TModel, string>> expression

任何帮助都将不胜感激。

你打算如何使用生成的表达式? - Nate Barbettini
var propertyMetadata = ModelMetadata.FromStringExpression(expression, helper.Html.ViewData); 变量 propertyMetadata 是从字符串表达式 expression 和 helper.Html.ViewData 中获取的模型元数据。 - InTheWorldOfCodingApplications
我需要以 model.[propertyName] 的形式呈现。 - InTheWorldOfCodingApplications
@Nkosi 当给定属性名称时,希望获得表达式 - InTheWorldOfCodingApplications
@在编程应用的世界中 https://msdn.microsoft.com/zh-cn/library/mt654263.aspx - Nkosi
3个回答

5

看起来你想调用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<Func<TModel, string>> - 最后一个类型可能是一个 int 或其他类型。此代码将构建适当的表达式树,无论目标属性类型如何。


这个能在ModelMetaData.FromLambdaExpression中使用吗? - InTheWorldOfCodingApplications
@InTheWorldOfCodingApplications 是的,那应该可以。如果不行,请告诉我。 - Nate Barbettini

5

请参考以下内容:

使用 API 创建表达式树

该文档介绍了如何通过使用 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...
}

"在变量参数中,model是什么? var parameter = Expression.Parameter(typeof(TModel), "model");" - InTheWorldOfCodingApplications
这是参数的名称。就像当您执行 x => x.propertyName 时,参数名称为 x。 - Nkosi

2

Expression只是一个包装lambda表达式的容器,用于创建树形数据结构。像HTML辅助程序这样的工具需要它来检查lambda以确定诸如属性名称之类的内容。类型的核心在于Func<TModel, string>,它表示它需要一个lambda,该lambda接受某种类型(泛型)的类实例并返回一个字符串(属性值)。换句话说:

m => m.Foo

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接