How to get metadata custom attributes?

后端 未结 5 505
南方客
南方客 2021-01-05 08:23

I have a class that defines data annotations at class level. The meta data class has custom attributes associated with it, along with the usual DisplayName,

相关标签:
5条回答
  • 2021-01-05 08:43

    Have you tried just doing this,

    public class BaseViewModel
    {
        [DisplayName("Id")]
        public int Id { get; set; }
    
        [DisplayName("Selected")]
        [ExportItem(Exclude = true)]
        public bool Selected { get; set; }
    }
    

    You can then use a variation of your code,

    var type = typeof(T);    
    var propertyMetaData = type.GetProperties()
        .Select(property => 
            property.GetCustomAttributes(typeof(ExportItemAttribute), false)
                    .FirstOrDefault() as ExportItemAttribute)
        .Where(attribute => attribute == null || !attribute.Exclude)
        .ToList();
    
    0 讨论(0)
  • 2021-01-05 08:46

    I offer the following solution as an alternative. It works with any custom attribute (this example demonstrates StringLength) and it's fast. It's based on this method and this method above.

    The MetaDataType attribute and type class:

    [MetadataType(typeof(ImportSetMetaData))]
    public partial class ImportSet
    {
    }
    
    public class ImportSetMetaData
    {
        [StringLength(maximumLength: 32)]
        public string Segment { get; set; }
    

    The extension methods:

    public static class Extension
    {
        private static int? GetMaxLength<T>(Expression<Func<T, string>> propertyExpression)
        {
            int? result = GetPropertyAttributeValue<T, string, StringLengthAttribute, int?>
                    (propertyExpression, attr => attr.MaximumLength);
            return result;
        }   
    
        public static int? GetMaxLength<T>(this T instance, Expression<Func<T, string>> propertyExpression)
        {
            return GetMaxLength<T>(propertyExpression);
        }
    
        private static TValue GetPropertyAttributeValue<T, TOut, TAttribute, TValue>
            (Expression<Func<T, TOut>> propertyExpression, Func<TAttribute, TValue> valueSelector) where TAttribute : Attribute
        {
            var expression = (MemberExpression)propertyExpression.Body;
            string mName = expression.Member.Name;
            Type type = typeof(T);
            MemberInfo member = type.GetMember(mName).FirstOrDefault();
            var attr = member.GetCustomAttribute<TAttribute>(inherit: true);
            if (attr != null)
            {
                return valueSelector(attr);
            }
            else
            {
                var mdTypeAttr = (MetadataTypeAttribute)type.GetCustomAttribute<MetadataTypeAttribute>(inherit: true);
                type = mdTypeAttr.MetadataClassType;
                member = type.GetMember(mName).FirstOrDefault();
                attr = member.GetCustomAttribute<TAttribute>(inherit: true);
                return (attr == null ? default(TValue) : valueSelector(attr));
            }
        }
    }
    

    The usage:

    int n = ImportSet.GetMaxLength(x => x.Segment);
    
    0 讨论(0)
  • 2021-01-05 08:53

    Found a solution by using the type of MetadataTypeAttribute to get the custom attributes.

    var type = typeof (T);
    var metadataType = type.GetCustomAttributes(typeof(MetadataTypeAttribute), true)
        .OfType<MetadataTypeAttribute>().FirstOrDefault();
    var metaData = (metadataType != null)
        ? ModelMetadataProviders.Current.GetMetadataForType(null, metadataType.MetadataClassType)
        : ModelMetadataProviders.Current.GetMetadataForType(null, type);
    
    var propertMetaData = metaData.Properties
        .Where(e =>
        {
            var attribute = metaData.ModelType.GetProperty(e.PropertyName)
                .GetCustomAttributes(typeof(ExportItemAttribute), false)
                .FirstOrDefault() as ExportItemAttribute;
            return attribute == null || !attribute.Exclude;
        })
        .ToList();
    
    0 讨论(0)
  • 2021-01-05 08:56

    Based on the other answers, i'm successed to get the DisplayName attribute from MetadataType class in this way:

    var metadataType = type.GetCustomAttributes(typeof(MetadataTypeAttribute), true)
                .OfType<MetadataTypeAttribute>().FirstOrDefault();
    
            var metaData = (metadataType != null)
                    ? ModelMetadataProviders.Current.GetMetadataForType(null, metadataType.MetadataClassType)
                    : ModelMetadataProviders.Current.GetMetadataForType(null, type);
    
            List<string> propertyList = metaData.Properties.
                Select(x => x.DisplayName).ToList();
    
    0 讨论(0)
  • 2021-01-05 09:02

    I was looking for something similar, and looking at the class MetadataTypeAttribute, I realice that it stores the type of the Metdata class. Inside that class, you can have get/set properties, or just fields (get/set properties are defined in one partial class, like autogenerated models in MVC), so, I read the fields within that metadata class, then get the atributes for the field. Code is:

        using System;
        using System.Linq;
        using System.Reflection;
        using System.ComponentModel.DataAnnotations;
    
        namespace PruebaAtributos
        {
            // Podemos ver la definición de 'MetaDataType' en:
            // https://referencesource.microsoft.com/#System.ComponentModel.DataAnnotations/DataAnnotations/MetadataTypeAttribute.cs,fb9a5881152a1584,references
            [MetadataType(typeof(ProgramMetadata))]
            partial class Program
            {
                // Campos de la clase
                public int Id { get; set; }
                public string Nombre { get; set; }
                public string Puesto { get; set; }
    
                static void Main(string[] args)
                {
                    Type t = typeof(Program);
    
                    // Atributos de la clase
                    Console.WriteLine("--- Atributos de clase: ");
                    Attribute[] attrs = Attribute.GetCustomAttributes(t);
                    foreach (Attribute at in attrs)
                    {
                        Console.WriteLine(at.GetType().Name);
                        if (at is MetadataTypeAttribute mdt)
                        {
                            // Nos interesa la información que contiene 'MetadataType'
                            Console.WriteLine($"--- Campos de {mdt.GetType().Name}:");
    
                            // Obtenemos las propiedades de la clase asociada con metadata type
                            var fields = mdt.MetadataClassType.GetFields();
                            foreach (FieldInfo fi in fields)
                            {
                                // Y mostramos los atributos asociados a cada uno de sus campos
                                var cas = fi.GetCustomAttributes(); // ca = Custom Attributes
                                Console.WriteLine($"   {fi.Name}.");
                                Console.WriteLine($"      attributos: {string.Join(", ", cas.Select(a => a.GetType().Name))}");
    
                                // Ahora consultamos la propiedad que deseamos de cada atributo conocido:
    
                                // Para consultar un attributo específico:
                                //DisplayAttribute da = (DisplayAttribute)ca.FirstOrDefault(a => a.GetType() == typeof(DisplayAttribute));
                                //if (da != null)
                                //{
                                //    Console.WriteLine($"   {da.GetType().Name}: {da.Name}");
                                //}
                                string desc;
                                foreach (var fa in cas) // fa = Field Attribute
                                {
                                    if (fa is ExportarAttribute exp)
                                    {
                                        // Conocemos las propiedades específicas de este 
                                        desc = $"{exp.GetType().Name}.exportar: {exp.exportar}";
                                    }
                                    else if (fa is MostrarAUsuario mau)
                                    {
                                        desc = $"{mau.GetType().Name}.mostrar: {mau.mostrar}";
                                    }
                                    else if (fa is DisplayAttribute da)
                                    {
                                        desc = $"{da.GetType().Name}.Name: {da.Name}";
                                    }
                                    else
                                    {
                                        desc = fa.GetType().Name;
                                    }
                                    Console.WriteLine($"      {desc}");
                                }
                            }
                        }
                    }
                }
            }
    
            // Attributos personalizados
            [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
            class MostrarAUsuario : Attribute
            {
                public readonly bool mostrar;
                public MostrarAUsuario(bool mostrar = true)
                {
                    this.mostrar = mostrar;
                }
            };
    
            class ExportarAttribute : Attribute
            {
                public readonly bool exportar;
                public ExportarAttribute(bool exportar = true)
                {
                    this.exportar = exportar;
                }
            }
    
            public class ProgramMetadata
            {
                // Display pertenece a MVC: System.ComponentModel.DataAnnotations
                [Display(Name = "Identificador"), MostrarAUsuario(false), Exportar(false), Phone]
                public int Id;
                [Display(Name = "Nombre completo"), MostrarAUsuario]
                public int Nombre;
                [Display(Name = "Puesto de trabajo"), Exportar]
                public int Puesto;
            }
        }
    

    Result I see is:

    0 讨论(0)
提交回复
热议问题