Получить атрибут [DisplayName] свойства строго типизированным способом - PullRequest
40 голосов
/ 29 марта 2011

Добрый день!

У меня есть такой метод, чтобы получить [DisplayName] значение атрибута свойства (которое прикрепляется напрямую или с использованием атрибута [MetadataType]). Я использую его в редких случаях, когда мне нужно получить [DisplayName] в коде контроллера.

public static class MetaDataHelper
{
    public static string GetDisplayName(Type dataType, string fieldName)
    {       
        // First look into attributes on a type and it's parents
        DisplayNameAttribute attr;
        attr = (DisplayNameAttribute)dataType.GetProperty(fieldName).GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();

        // Look for [MetadataType] attribute in type hierarchy
        // /931378/attribute-isdefined-ne-vidit-atributy-primenennye-s-klassom-metadatatype
        if (attr == null)
        {
            MetadataTypeAttribute metadataType = (MetadataTypeAttribute)dataType.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
            if (metadataType != null)
            {
                var property = metadataType.MetadataClassType.GetProperty(fieldName);
                if (property != null)
                {
                    attr = (DisplayNameAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
                }
            }
        }
        return (attr != null) ? attr.DisplayName : String.Empty;
    }
}

Работает, но имеет два недостатка:

  • Требуется имя поля в виде строки
  • Это не работает, если я хочу получить свойство собственности

Можно ли преодолеть обе проблемы с помощью лямбды, как у нас в ASP.NET MVC:

Html.LabelFor(m => m.Property.Can.Be.Very.Complex.But.Strongly.Typed);  

Обновление

Вот обновленная и проверенная версия решения BuildStarted . Он модифицирован для использования атрибута DisplayName (вы можете изменить его обратно на атрибут Display, если используете его). И исправлены незначительные ошибки, чтобы получить атрибут вложенных свойств.

public static string GetDisplayName<TModel>(Expression<Func<TModel, object>> expression)
{
    Type type = typeof(TModel);

    string propertyName = null;
    string[] properties = null;
    IEnumerable<string> propertyList;
    //unless it's a root property the expression NodeType will always be Convert
    switch (expression.Body.NodeType)
    {
        case ExpressionType.Convert:
        case ExpressionType.ConvertChecked:
            var ue = expression.Body as UnaryExpression;
            propertyList = (ue != null ? ue.Operand : null).ToString().Split(".".ToCharArray()).Skip(1); //don't use the root property
            break;
        default:
            propertyList = expression.Body.ToString().Split(".".ToCharArray()).Skip(1);
            break;
    }

    //the propert name is what we're after
    propertyName = propertyList.Last();
    //list of properties - the last property name
    properties = propertyList.Take(propertyList.Count() - 1).ToArray(); //grab all the parent properties

    foreach (string property in properties)
    {
        PropertyInfo propertyInfo = type.GetProperty(property);
        type = propertyInfo.PropertyType;
    }

    DisplayNameAttribute attr;
    attr = (DisplayNameAttribute)type.GetProperty(propertyName).GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();

    // Look for [MetadataType] attribute in type hierarchy
    // /931378/attribute-isdefined-ne-vidit-atributy-primenennye-s-klassom-metadatatype
    if (attr == null)
    {
        MetadataTypeAttribute metadataType = (MetadataTypeAttribute)type.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
        if (metadataType != null)
        {
            var property = metadataType.MetadataClassType.GetProperty(propertyName);
            if (property != null)
            {
                attr = (DisplayNameAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
            }
        }
    }
    return (attr != null) ? attr.DisplayName : String.Empty;
}

Ответы [ 7 ]

39 голосов
/ 29 марта 2011

Есть два способа сделать это:

Models.Test test = new Models.Test();
string DisplayName = test.GetDisplayName(t => t.Name);

string DisplayName = Helpers.GetDisplayName<Models.Test>(t => t.Name);

Первый работает на основе написания универсального метода расширения для любой TModel (которая является всеми типами). Это означает, что он будет доступен для любого объекта, а не только для вашей модели. Не очень рекомендуется, но приятно из-за краткого синтаксиса.

Второй метод требует, чтобы вы передали тип модели, которую вы уже используете, - но вместо этого вы используете ее в качестве параметра. Этот метод необходим для определения типа через Generics, потому что Func ожидает его.

Вот методы, которые вы можете проверить.

Метод статического расширения для всех объектов

public static string GetDisplayName<TModel, TProperty>(this TModel model, Expression<Func<TModel, TProperty>> expression) {

    Type type = typeof(TModel);

    MemberExpression memberExpression = (MemberExpression)expression.Body;
    string propertyName = ((memberExpression.Member is PropertyInfo) ? memberExpression.Member.Name : null);

    // First look into attributes on a type and it's parents
    DisplayAttribute attr;
    attr = (DisplayAttribute)type.GetProperty(propertyName).GetCustomAttributes(typeof(DisplayAttribute), true).SingleOrDefault();

    // Look for [MetadataType] attribute in type hierarchy
    // /931378/attribute-isdefined-ne-vidit-atributy-primenennye-s-klassom-metadatatype
    if (attr == null) {
        MetadataTypeAttribute metadataType = (MetadataTypeAttribute)type.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
        if (metadataType != null) {
            var property = metadataType.MetadataClassType.GetProperty(propertyName);
            if (property != null) {
                attr = (DisplayAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
            }
        }
    }
    return (attr != null) ? attr.Name : String.Empty;


}

Подпись для конкретного типа метода - тот же код, что и выше, только другой вызов

public static string GetDisplayName<TModel>(Expression<Func<TModel, object>> expression) { }

Причина, по которой вы не можете просто использовать Something.GetDisplayName(t => t.Name) сама по себе, состоит в том, что в движке Razor вы фактически передаете экземплярный объект HtmlHelper<TModel>, поэтому первый метод требует создания экземпляра объекта - это требуется только чтобы компилятор мог определить, какие типы принадлежат какому универсальному имени.

Обновление с рекурсивными свойствами

public static string GetDisplayName<TModel>(Expression<Func<TModel, object>> expression) {

    Type type = typeof(TModel);

    string propertyName = null;
    string[] properties = null;
    IEnumerable<string> propertyList;
    //unless it's a root property the expression NodeType will always be Convert
    switch (expression.Body.NodeType) {
        case ExpressionType.Convert:
        case ExpressionType.ConvertChecked:
            var ue = expression.Body as UnaryExpression;
            propertyList = (ue != null ? ue.Operand : null).ToString().Split(".".ToCharArray()).Skip(1); //don't use the root property
            break;
        default:
            propertyList = expression.Body.ToString().Split(".".ToCharArray()).Skip(1);
            break;
    }

    //the propert name is what we're after
    propertyName = propertyList.Last();
    //list of properties - the last property name
    properties = propertyList.Take(propertyList.Count() - 1).ToArray(); //grab all the parent properties

    Expression expr = null;
    foreach (string property in properties) {
        PropertyInfo propertyInfo = type.GetProperty(property);
        expr = Expression.Property(expr, type.GetProperty(property));
        type = propertyInfo.PropertyType;
    }

    DisplayAttribute attr;
    attr = (DisplayAttribute)type.GetProperty(propertyName).GetCustomAttributes(typeof(DisplayAttribute), true).SingleOrDefault();

    // Look for [MetadataType] attribute in type hierarchy
    // /931378/attribute-isdefined-ne-vidit-atributy-primenennye-s-klassom-metadatatype
    if (attr == null) {
        MetadataTypeAttribute metadataType = (MetadataTypeAttribute)type.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
        if (metadataType != null) {
            var property = metadataType.MetadataClassType.GetProperty(propertyName);
            if (property != null) {
                attr = (DisplayAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
            }
        }
    }
    return (attr != null) ? attr.Name : String.Empty;



}
31 голосов
/ 04 июня 2012

Поздно к игре, но ...

Я создал вспомогательный метод с использованием ModelMetadata, как упомянуто @Daniel, и я решил поделиться им:

public static string GetDisplayName<TModel, TProperty>(
      this TModel model
    , Expression<Func<TModel, TProperty>> expression)
{
    return ModelMetadata.FromLambdaExpression<TModel, TProperty>(
        expression,
        new ViewDataDictionary<TModel>(model)
        ).DisplayName;
}

Пример использования:

Models:

public class MySubObject
{
    [DisplayName("Sub-Awesome!")]
    public string Sub { get; set; }
}

public class MyObject
{
    [DisplayName("Awesome!")]
    public MySubObject Prop { get; set; }
}

Use:

HelperNamespace.GetDisplayName(Model, m => m.Prop) // "Awesome!"
HelperNamespace.GetDisplayName(Model, m => m.Prop.Sub) // "Sub-Awesome!"
4 голосов
/ 30 ноября 2015

Просто сделайте это:

using System.ComponentModel;
using System.Linq;
using System.Reflection;

namespace yournamespace
{
    public static class ExtensionMethods
    {
        public static string GetDisplayName(this PropertyInfo prop)
        {
            if (prop.CustomAttributes == null || prop.CustomAttributes.Count() == 0)
                return prop.Name;

            var displayNameAttribute = prop.CustomAttributes.Where(x => x.AttributeType == typeof(DisplayNameAttribute)).FirstOrDefault();

            if (displayNameAttribute == null || displayNameAttribute.ConstructorArguments == null || displayNameAttribute.ConstructorArguments.Count == 0)
                return prop.Name;

            return displayNameAttribute.ConstructorArguments[0].Value.ToString() ?? prop.Name;
        }
    }
}

Пример по запросу:

var props = typeof(YourType).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanRead);

var propFriendlyNames = props.Select(x => x.GetDisplayName());
2 голосов
/ 03 октября 2014

Я полностью согласен с решением, предоставленным BuildStarted.Единственное, что я хотел бы изменить, это то, что ExtensionsMethode не поддерживает переводы.Чтобы поддержать это просто, необходимы незначительные изменения.Я бы поставил это в комментариях, но у меня недостаточно очков для этого.Найдите последнюю строку в методе.

Метод расширения

public static string GetDisplayName<TModel, TProperty>(this TModel model, Expression<Func<TModel, TProperty>> expression)
{
        Type type = typeof(TModel);
        IEnumerable<string> propertyList;

        //unless it's a root property the expression NodeType will always be Convert
        switch (expression.Body.NodeType)
        {
            case ExpressionType.Convert:
            case ExpressionType.ConvertChecked:
                var ue = expression.Body as UnaryExpression;
                propertyList = (ue != null ? ue.Operand : null).ToString().Split(".".ToCharArray()).Skip(1); //don't use the root property
                break;
            default:
                propertyList = expression.Body.ToString().Split(".".ToCharArray()).Skip(1);
                break;
        }

        //the propert name is what we're after
        string propertyName = propertyList.Last();
        //list of properties - the last property name
        string[] properties = propertyList.Take(propertyList.Count() - 1).ToArray();

        Expression expr = null;
        foreach (string property in properties)
        {
            PropertyInfo propertyInfo = type.GetProperty(property);
            expr = Expression.Property(expr, type.GetProperty(property));
            type = propertyInfo.PropertyType;
        }

        DisplayAttribute attr = (DisplayAttribute)type.GetProperty(propertyName).GetCustomAttributes(typeof(DisplayAttribute), true).SingleOrDefault();

        // Look for [MetadataType] attribute in type hierarchy
        // /931378/attribute-isdefined-ne-vidit-atributy-primenennye-s-klassom-metadatatype
        if (attr == null)
        {
            MetadataTypeAttribute metadataType = (MetadataTypeAttribute)type.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
            if (metadataType != null)
            {
                var property = metadataType.MetadataClassType.GetProperty(propertyName);
                if (property != null)
                {
                    attr = (DisplayAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
                }
            }
        }
        //To support translations call attr.GetName() instead of attr.Name
        return (attr != null) ? attr.GetName() : String.Empty;
 }
1 голос
/ 15 сентября 2016

Я нашел еще один хороший фрагмент кода здесь , и я немного изменил его для цели 'DisplayName'

    public static string GetDisplayName<TSource, TProperty>(Expression<Func<TSource, TProperty>> expression)
    {
        var attribute = Attribute.GetCustomAttribute(((MemberExpression)expression.Body).Member, typeof(DisplayNameAttribute)) as DisplayNameAttribute;

        if (attribute == null)
        {
            throw new ArgumentException($"Expression '{expression}' doesn't have DisplayAttribute");
        }

        return attribute.DisplayName;
    }

И использования

GetDisplayName<ModelName, string>(i => i.PropertyName)
1 голос
/ 25 августа 2015

Я немного изменил, если вы используете ресурсы для получения DisplayName

    public static string GetDisplayName<TModel>(Expression<Func<TModel, object>> expression)
  {

     string _ReturnValue = string.Empty;

     Type type = typeof(TModel);

     string propertyName = null;
     string[] properties = null;
     IEnumerable<string> propertyList;
     //unless it's a root property the expression NodeType will always be Convert
     switch (expression.Body.NodeType)
     {
        case ExpressionType.Convert:
        case ExpressionType.ConvertChecked:
           var ue = expression.Body as UnaryExpression;
           propertyList = (ue != null ? ue.Operand : null).ToString().Split(".".ToCharArray()).Skip(1); //don't use the root property
           break;
        default:
           propertyList = expression.Body.ToString().Split(".".ToCharArray()).Skip(1);
           break;
     }

     //the propert name is what we're after
     propertyName = propertyList.Last();
     //list of properties - the last property name
     properties = propertyList.Take(propertyList.Count() - 1).ToArray(); //grab all the parent properties

     Expression expr = null;
     foreach (string property in properties)
     {
        PropertyInfo propertyInfo = type.GetProperty(property);
        expr = Expression.Property(expr, type.GetProperty(property));
        type = propertyInfo.PropertyType;
     }

     DisplayAttribute attr;
     attr = (DisplayAttribute)type.GetProperty(propertyName).GetCustomAttributes(typeof(DisplayAttribute), true).SingleOrDefault();

     // Look for [MetadataType] attribute in type hierarchy
     // /931378/attribute-isdefined-ne-vidit-atributy-primenennye-s-klassom-metadatatype
     if (attr == null)
     {
        MetadataTypeAttribute metadataType = (MetadataTypeAttribute)type.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
        if (metadataType != null)
        {
           var property = metadataType.MetadataClassType.GetProperty(propertyName);
           if (property != null)
           {
              attr = (DisplayAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
           }
        }
     }

     if (attr != null && attr.ResourceType != null)
        _ReturnValue = attr.ResourceType.GetProperty(attr.Name).GetValue(attr).ToString();
     else if (attr != null)
        _ReturnValue = attr.Name;

     return _ReturnValue;
  }

Happy Coding

0 голосов
/ 15 сентября 2016

Другой фрагмент кода с кодом .Net использует себя для выполнения этого

public static class WebModelExtensions
{
    public static string GetDisplayName<TModel, TProperty>(
      this HtmlHelper<TModel> html, 
      Expression<Func<TModel, TProperty>> expression)
    {
        // Taken from LabelExtensions
        var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);

        string displayName = metadata.DisplayName;
        if (displayName == null)
        {
            string propertyName = metadata.PropertyName;
            if (propertyName == null)
            {
                var htmlFieldName = ExpressionHelper.GetExpressionText(expression);
                displayName = ((IEnumerable<string>) htmlFieldName.Split('.')).Last<string>();
            }
            else
                displayName = propertyName;
        }

        return displayName;
    }
}
// Usage
Html.GetDisplayName(model => model.Password)
...