Attribute.IsDefined не видит атрибуты, примененные с классом MetadataType - PullRequest
21 голосов
/ 16 декабря 2009

Если я применю атрибуты к частичному классу через атрибут MetadataType , эти атрибуты не будут найдены с помощью Attribute.IsDefined () . Кто-нибудь знает почему, или что я делаю не так?

Ниже приведен тестовый проект, который я создал для этого, но я действительно пытаюсь применить пользовательские атрибуты к классу сущностей LINQ to SQL - как этот ответ в этом вопросе .

Спасибо!

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

Ответы [ 4 ]

22 голосов
/ 16 декабря 2009

Атрибут MetadataType используется для указания справки, чтобы указать дополнительную информацию об объекте данных. Для доступа к дополнительным атрибутам вам нужно сделать что-то вроде следующего:

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

Сюда также входит пример атрибута, показывающий, как извлечь добавленную информацию.

3 голосов
/ 04 декабря 2010

У меня была похожая ситуация. Я закончил тем, что написал следующий метод расширения для этого. Идея состоит в том, чтобы скрыть абстракцию просмотра в 2 местах (основной класс и класс метаданных).

    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;
    }
0 голосов
/ 09 ноября 2017

Даны следующие классы:

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

Мне нужно было узнать, определено ли Key в свойстве для класса Person. Затем мне нужно было получить стоимость имущества. Используя ответ @AdamGrid, я изменил код следующим образом:

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.");
}
0 голосов
/ 09 июня 2015

Мое решение для общего использования. Получите атрибут свойства, которое вы ищете. Вернуть ноль, если не найден.

Если найдено, он возвращает сам атрибут. Таким образом, вы можете иметь доступ к свойствам внутри атрибута, если хотите.

Надеется на эту помощь.

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