如何获取元数据自定义属性?

时间:2014-06-02 14:09:09

标签: c# reflection data-annotations

我有一个在课程级别定义数据注释的课程。元数据类具有与之关联的自定义属性,以及通常的DisplayName,DisplayFormat等。

public class BaseMetaData
{
    [DisplayName("Id")]
    public object Id { get; set; }

    [DisplayName("Selected")]
    [ExportItem(Exclude = true)]
    public object Selected { get; set; }
}

[MetadataType(typeof(BaseMetaData))]
public class BaseViewModel
{
    public int Id { get; set; }
    public bool Selected { get; set; }

如果类型 T ,如何从元数据类中检索自定义属性?下面的尝试不起作用,因为元数据属性来自 BaseViewModel 而不是 BaseMetaData 类。

需要一般工作,即不能做typeof(BaseMetaData).GetProperty(e.PropertyName)。想知道是否有从类中获取MetadataType的方法,那么它就可以实现。

var type = typeof (T);
var metaData = ModelMetadataProviders.Current.GetMetadataForType(null, type);

var propertMetaData = metaData.Properties
    .Where(e =>
    {
        var attribute = type.GetProperty(e.PropertyName)
            .GetCustomAttributes(typeof(ExportItemAttribute), false)
            .FirstOrDefault() as ExportItemAttribute;
        return attribute == null || !attribute.Exclude;
    })
    .ToList();

5 个答案:

答案 0 :(得分:15)

使用MetadataTypeAttribute类型找到解决方案以获取自定义属性。

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

答案 1 :(得分:0)

你有没有试过这样做,

public class BaseViewModel
{
    [DisplayName("Id")]
    public int Id { get; set; }

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

然后,您可以使用代码的变体

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

答案 2 :(得分:0)

我一直在寻找类似的东西,然后查看类MetadataTypeAttribute,我意识到它存储了Metdata类的类型。在该类内部,您可以具有get / set属性,也可以仅具有字段(在一个局部类中定义了get / set属性,例如MVC中的自动生成的模型),因此,我读取了该元数据类中的字段,然后获取其属性场。代码是:

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

我看到的结果是:Console result

答案 3 :(得分:0)

基于其他答案,我成功地从MetadataType类获取DisplayName属性 通过这种方式:

protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
        {
            return new[]
            {
                new ServiceInstanceListener(
                    context =>
                    new FabricTransportServiceRemotingListener(
                        context,
                        serviceRemotingMessageHandler: new MiddlewareServiceRemotingMessageDispatcher(
                            context,
                            this,
                            middlewareAction: MiddlewareAction,
                            serviceRemotingMessageBodyFactory: new JsonMessageFactory()),
                        serializationProvider: new ServiceRemotingJsonSerializationProvider()))
            };
    }

答案 4 :(得分:0)

我提供以下解决方案作为替代。它可以与任何自定义属性一起使用(此示例演示了StringLength),并且速度很快。它基于上面的method和上面的method

MetaDataType属性和类型类:

[MetadataType(typeof(ImportSetMetaData))]
public partial class ImportSet
{
}

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

扩展方法:

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

用法:

int n = ImportSet.GetMaxLength(x => x.Segment);