Pergunta

Se eu aplicar atributos a uma classe parcial através do atributo MetadataType , esses atributos não são encontrados através de Attribute.IsDefined () . conhecia ninguém por que, ou o que estou fazendo de errado?

Abaixo está um projeto de teste que eu criei para isso, mas eu realmente estou tentando aplicar atributos para um LINQ to SQL classe de entidade personalizada - como esta resposta nesta questão .

Obrigado!

using System;
using System.ComponentModel.DataAnnotations;
using System.Reflection;

namespace MetaDataTest
{
    class Program
    {
        static void Main(string[] args)
        {
            PropertyInfo[] properties = typeof(MyTestClass).GetProperties();

            foreach (PropertyInfo propertyInfo in properties)
            {
                Console.WriteLine(Attribute.IsDefined(propertyInfo, typeof(MyAttribute)));
                Console.WriteLine(propertyInfo.IsDefined(typeof(MyAttribute), true));
                Console.WriteLine(propertyInfo.GetCustomAttributes(true).Length);

                // Displays:
                // False
                // False
                // 0
            }

            Console.ReadLine();
        }
    }

    [MetadataType(typeof(MyMeta))]
    public partial class MyTestClass
    {
        public string MyField { get; set; }
    }

    public class MyMeta
    {
        [MyAttribute()]
        public string MyField { get; set; }
    }

    [AttributeUsage(AttributeTargets.All)]
    public class MyAttribute : System.Attribute
    {
    }
}
Foi útil?

Solução

O atributo MetadataType é usado para especificar ajuda especificar as informações adicionais sobre o objeto de dados. Para acessar os atributos adicionais que você precisa fazer algo como o seguinte:

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

namespace MetaDataTest
{
    class Program
    {
        static void Main(string[] args)
        {
            MetadataTypeAttribute[] metadataTypes = typeof(MyTestClass).GetCustomAttributes(typeof(MetadataTypeAttribute), true).OfType<MetadataTypeAttribute>().ToArray();
            MetadataTypeAttribute metadata = metadataTypes.FirstOrDefault();

            if (metadata != null)
            {
                PropertyInfo[] properties = metadata.MetadataClassType.GetProperties();

                foreach (PropertyInfo propertyInfo in properties)
                {
                    Console.WriteLine(Attribute.IsDefined(propertyInfo, typeof(MyAttribute)));
                    Console.WriteLine(propertyInfo.IsDefined(typeof(MyAttribute), true));
                    Console.WriteLine(propertyInfo.GetCustomAttributes(true).Length);
                    RequiredAttribute attrib = (RequiredAttribute)propertyInfo.GetCustomAttributes(typeof(RequiredAttribute), true)[0];
                    Console.WriteLine(attrib.ErrorMessage);
                }

                // Results:
                // True
                // True
                // 2
                // MyField is Required
            }

            Console.ReadLine();
        }
    }

    [MetadataType(typeof(MyMeta))]
    public partial class MyTestClass
    {
        public string MyField { get; set; }
    }

    public class MyMeta
    {
        [MyAttribute()]
        [Required(ErrorMessage="MyField is Required")]
        public string MyField { get; set; }
    }

    [AttributeUsage(AttributeTargets.All)]
    public class MyAttribute : System.Attribute
    {
    }
}

Isto também inclui um atributo de exemplo para mostrar como extrair informação que foi adicionado.

Outras dicas

Eu tinha uma situação semelhante. Acabei escrevendo o seguinte método de extensão para ele. A ideia é esconder a abstração de olhar em 2 lugares (classe principal e classe de metadados).

    static public Tattr GetSingleAttribute<Tattr>(this PropertyInfo pi, bool Inherit = true) where Tattr : Attribute
    {
        var attrs = pi.GetCustomAttributes(typeof(Tattr), Inherit);
        if (attrs.Length > 0)
            return (Tattr)attrs[0];
        var mt = pi.DeclaringType.GetSingleAttribute<MetadataTypeAttribute>();
        if (mt != null)
        {
            var pi2 = mt.MetadataClassType.GetProperty(pi.Name);
            if (pi2 != null)
                return pi2.GetSingleAttribute<Tattr>(Inherit);
        }
        return null;
    }

A minha solução para uso genérico. Obter o atributo da propriedade que você está procurando. Return null se não for encontrado.

Se encontrado, ele retorna o próprio atributo. Então você pode ter acesso às propriedades dentro do atributo se você WIHS.

Espera que isso ajuda.

public static Attribute GetAttribute<T>(this PropertyInfo PI, T t) where T: Type
{
    var Attrs = PI.DeclaringType.GetCustomAttributes(typeof(MetadataTypeAttribute), true);
    if (Attrs.Length < 1) return null;

    var metaAttr = Attrs[0] as MetadataTypeAttribute;
    var metaProp = metaAttr.MetadataClassType.GetProperty(PI.Name);
    if (metaProp == null) return null;

    Attrs = metaProp.GetCustomAttributes(t, true);
    if (Attrs.Length < 1) return null;
    return Attrs[0] as Attribute;
}

Dadas as seguintes classes:

public partial class Person
{
    public int PersonId { get; set; }
}

[MetadataType(typeof(PersonMetadata))]
public partial class Person
{
    public partial class PersonMetadata
    {
        [Key]
        public int PersonId { get; set; }
    }
}

Eu precisava ver se Key foi definido em uma propriedade de classe Person. Eu, então, necessário para obter o valor da propriedade. Usando resposta @AdamGrid, modifiquei o código como este para obtê-lo:

private static object GetPrimaryKeyValue(TEntity entity)
{
    MetadataTypeAttribute[] metadataTypes = typeof(TEntity).GetCustomAttributes(typeof(MetadataTypeAttribute), true).OfType<MetadataTypeAttribute>().ToArray();
    MetadataTypeAttribute metadata = metadataTypes.FirstOrDefault();
    if (metadata == null)
    {
        ThrowNotFound();
    }

    PropertyInfo[] properties = metadata.MetadataClassType.GetProperties();
    PropertyInfo primaryKeyProperty =
        properties.SingleOrDefault(x => Attribute.GetCustomAttribute(x, typeof(KeyAttribute)) as KeyAttribute != null);
    if (primaryKeyProperty == null)
    {
        ThrowNotFound();
    }

    object primaryKeyValue = typeof(TEntity).GetProperties().Single(x => x.Name == primaryKeyProperty.Name).GetValue(entity);

    return primaryKeyValue;
}

private static void ThrowNotFound()
{
    throw new InvalidOperationException
            ($"The type {typeof(TEntity)} does not have a property with attribute KeyAttribute to indicate the primary key. You must add that attribute to one property of the class.");
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top