Attribute.IsDefined не видит атрибуты, примененные с классом MetadataType.
-
19-09-2019 - |
Вопрос
Если я применяю атрибуты к частичному классу через Атрибут MetadataType, эти атрибуты не найдены через Атрибут.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
{
}
}
Решение
А 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
{
}
}
Сюда также входит образец атрибута, показывающий, как извлечь добавленную информацию.
Другие советы
У меня была похожая ситуация.В итоге я написал для него следующий метод расширения.Идея состоит в том, чтобы скрыть абстракцию поиска в двух местах (основной класс и класс метаданных).
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;
}
Мое решение для общего использования.Получите атрибут свойства, которое вы ищете.Верните ноль, если не найден.
Если он найден, он возвращает сам атрибут.Таким образом, вы можете иметь доступ к свойствам внутри атрибута, если хотите.
Надеется, что это поможет.
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;
}
Учитывая следующие классы:
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.");
}