如何创建表达式<func <tmodel,string =“”>&gt;属性名称中的表达式

时间:2017-05-01 12:38:08

标签: c# asp.net-mvc lambda

My Html helper方法如下所示

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

我想将我的属性名称转换为以下

 Expression<Func<TModel, string>> expression

非常感谢任何帮助

3 个答案:

答案 0 :(得分:3)

您似乎想要拨打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或其他类型。无论目标属性类型如何,此代码都将构建正确的表达式树。

答案 1 :(得分:3)

参考以下内容以供参考

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...
}

答案 2 :(得分:1)

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

m => m.Foo

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