Get [DisplayName] attribute of a property in strongly-typed way

后端 未结 8 1710
猫巷女王i
猫巷女王i 2020-12-02 08:43

Good day!

I\'ve such method to get [DisplayName] attribute value of a property (which is attached directly or using [MetadataType] attribut

相关标签:
8条回答
  • 2020-12-02 08:48

    @Buildstarted Answer works but i wanted to get the DisplayName by property name rather using linq expression so i did a little change that will save your time

    public static string GetDisplayNameByMemberName<TModel>(string memberName)
        {
            Type type = typeof(TModel);
            string propertyName = memberName;
    
            DisplayAttribute attr;
            attr = (DisplayAttribute)type.GetProperty(propertyName).GetCustomAttributes(typeof(DisplayAttribute), true).SingleOrDefault();
    
            //Look for [MetadataType] attribute in type hierarchy
            //http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class
            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;
        }
    

    I also wanted to get the localized value in the resources (.resx files) so :

    string displayName = GeneralHelper.GetDisplayNameByMemberName<ViewModels.ProductVM>(memberName);
    string displayNameTranslated = resourceManager.GetString(displayName, MultiLangHelper.CurrentCultureInfo);
    

    This is a helper function i made to return an object of type "CultureInfo", create a culture info of some language or pass the current one

    //MultiLangHelper.CurrentCultureInfo
    return System.Threading.Thread.CurrentThread.CurrentCulture;
    
    0 讨论(0)
  • 2020-12-02 08:53

    I totally agree with the solution BuildStarted provided. The only thing i would change is that the ExtensionsMethode does not support translations. To support this is simple minor change is needed. I would have put this in the comments but i don't have enough points to do so. Look for the last line in the method.

    The Extension Method

    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
            // http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class
            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;
     }
    
    0 讨论(0)
  • 2020-12-02 08:53

    I found another nice code snippet here, and I have slightly modified it for 'DisplayName' purpose

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

    And usages

    GetDisplayName<ModelName, string>(i => i.PropertyName)
    
    0 讨论(0)
  • 2020-12-02 08:57

    I make a Little change is you are using resources to get the 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
         // http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class
         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 讨论(0)
  • 2020-12-02 08:58

    There are two ways to do this:

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

    The first one works by virtue of writing a generic extension method to any TModel (which is all types). This means it will be available on any object and not just your model. Not really recommended but nice because of it's concise syntax.

    The second method requires you to pass in the Type of the model it is - which you're already doing but as a parameter instead. This method is required to define type via Generics because Func expects it.

    Here are the methods for you to check out.

    Static extension method to all objects

    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
        // http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class
        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;
    
    
    }
    

    Signature for type specific method - same code as above just different call

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

    The reason you can't just use Something.GetDisplayName(t => t.Name) on it's own is because in the Razor engine you're actually passing an instantiated object of HtmlHelper<TModel> which is why the first method requires an instantiated object - This is only required for the compiler to infer what types belong to which generic name.

    Update with recursive properties

    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
        // http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class
        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;
    
    
    
    }
    
    0 讨论(0)
  • 2020-12-02 09:00

    Just do this:

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

    Example as requested:

    var props = typeof(YourType).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanRead);
    
    var propFriendlyNames = props.Select(x => x.GetDisplayName());
    
    0 讨论(0)
提交回复
热议问题