Как получить доступ к собственности из лямбды - PullRequest
0 голосов
/ 27 марта 2019

Я бы хотел реорганизовать этот код, чтобы избавиться от необходимости полагаться на TModel, TValue

public static string DescriptionFor<TModel, TValue>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{
    // null checks

    DescriptionAttribute descriptionAttribute = null;
    if (expression.Body is MemberExpression memberExpression)
    {
        descriptionAttribute = memberExpression.Member
            .GetCustomAttributes(typeof(DescriptionAttribute), false)
            .Cast<DescriptionAttribute>()
            .SingleOrDefault();
    }

    return descriptionAttribute?.Description ?? string.Empty;
}

, который имеет такой вызов:

@Html.DescriptionFor(x => x.MyModel.MyOtherModel.Property)

на что-то подобное

public static string DescriptionFor2<T>(Expression<Func<T>> expression)
{
    if (expression == null)
        throw new ArgumentNullException(nameof(expression));

    if (!(expression.Body is MemberExpression memberExpression))
    {
        return string.Empty;
    }

    foreach (var property in typeof(T).GetProperties())
    {
        if (property == ((expression.Body as MemberExpression).Member as PropertyInfo))
        {
            var attr = property
                        .GetCustomAttributes(typeof(DescriptionAttribute), false)
                        .Cast<DescriptionAttribute>()
                        .FirstOrDefault();

            return attr?.Description ?? string.Empty;
        }
    }

    return string.Empty;       
}

с таким вызовом:

@MyHtml.DescriptionFor2<MyOtherModel>(x => x.MyProperty);

Но здесь ошибка:

Делегат Func не принимает 1 аргумент

1 Ответ

0 голосов
/ 27 марта 2019

Вы можете сделать это:

void Main()
{
    Console.WriteLine(MyHtml.DescriptionFor2<Test>((t) => t.StringMember));
    Console.WriteLine(MyHtml.DescriptionFor2<Test>((t) => t.BooleanMember));
    Console.WriteLine(MyHtml.DescriptionFor2<Test>((t) => t.IntegerMember));
}

public class Test
{
    [Description("This is a string member")]
    public string StringMember { get; set; }
    [Description("This is a boolean member")]
    public bool BooleanMember { get; set; }
    [Description("This is a integer member")]
    public int IntegerMember { get; set; }
}

public static class MyHtml
{
    public static string DescriptionFor2<T>(Expression<Func<T, dynamic>> expression)
    {
        var result = string.Empty;
        var member = GetMemberExpression(expression)?.Member?.Name;
        if (member != null)
        {
            var property = typeof(T).GetProperty(member);
            if (property != null)
            {
                var attr = property
                            .GetCustomAttributes(typeof(DescriptionAttribute), false)
                            .Cast<DescriptionAttribute>()
                            .FirstOrDefault();

                result = attr?.Description ?? string.Empty;
            }
        }

        return result;
    }

    private static MemberExpression GetMemberExpression<T>(Expression<Func<T, dynamic>> expression)
    {
        var member = expression.Body as MemberExpression;
        var unary = expression.Body as UnaryExpression;
        return member ?? (unary != null ? unary.Operand as MemberExpression : null);
    }
}

public class DescriptionAttribute : Attribute
{
    public string Description { get; set; }

    public DescriptionAttribute(string description)
    {
        Description = description;
    }
}

Это включает получение тела выражения в качестве члена и использование его имени для разрешения свойства объекта.Тип данных dynamic используется для избавления от параметра второго типа, однако вы также можете определить его явно для лучшего обнаружения ошибок во время компиляции:

public static string DescriptionFor2<T, U>(Expression<Func<T, U>> expression)

и вызова его:

MyHtml.DescriptionFor2<Test, string>((t) => t.StringMember);
MyHtml.DescriptionFor2<Test, bool>((t) => t.BooleanMember);
MyHtml.DescriptionFor2<Test, int>((t) => t.IntegerMember);

РЕДАКТИРОВАТЬ: отредактированный ответ, поскольку первоначальное решение не работало для типов значений, см. Выражение для членов типа приводит к различным выражениям (MemberExpression, UnaryExpression)

...