How to create expression <Func <TModel, string >> expression from property name

MyHtml helper method looks like this

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

      

I want to convert my property name to the following

 Expression<Func<TModel, string>> expression

      

Any help would be much appreciated

+3


source to share


3 answers


It sounds like you want to call ModelMetadata.FromLambdaExpression

, not FromStringExpression

. You can create an expression like

x => x.PropertyName

      

from scratch, for example:

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

      



To make the code more readable, manipulation of the expression tree with nullity is moved to this helper class:

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

      

Building an expression tree from scratch gives you the most flexibility when creating a lambda expression. Depending on the type of the target property, it may not always be Expression<Func<TModel, string>>

- the latter type may be int

or something else. This code will build the correct expression tree regardless of the target property type.

+3


source


Referring to the following for reference

Creating Expression Trees 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;
}

      



Which would allow you to get the expression in the helper

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

      

+3


source


Expression

is just a wrapper around a lambda that creates a tree-style data structure. Things like HTML helpers need this so they can examine the lambda to figure out things like the property name. The meat of this type is in Func<TModel, string>

, indicating that it requires a lambda that takes an instance of a class of some type (generic) and returns a string (property value). In other words:

m => m.Foo

      

Where m

is the parameter for the lambda, and most likely it will be executed by passing in your model. m

, here, is similar to a typed parameter for a normal method, so it can be called anything that can be called any other variable. Then the return value is Model.Foo

, where Foo

is the property you are accessing.

+1


source







All Articles