2009-12-15 4 views
21

Si je demande des attributs à une classe partielle par le MetadataType attribute, ces attributs ne sont pas trouvés par Attribute.IsDefined(). Quelqu'un sait pourquoi, ou ce que je fais mal? Voici un projet de test que j'ai créé pour cela, mais j'essaie vraiment d'appliquer des attributs personnalisés à une classe d'entités LINQ to SQL - comme this answer in this question.Attribute.IsDefined ne voit pas les attributs appliqués avec classe MetadataType

Merci!

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

vérifier ce ceci, je l'ai déjà répondu à cette question ici http://stackoverflow.com/a/24757520/3050647 – elia07

+0

vérifier cette ceci dehors, j'ai déjà répondu à cette question ici http://stackoverflow.com/a/24757520/3050647 – elia07

Répondre

22

L'attribut MetadataType est utilisé pour spécifier l'aide spécifier les informations supplémentaires sur l'objet de données. Pour accéder aux attributs supplémentaires que vous devez faire quelque chose comme ce qui suit:

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

Cela inclut également un attribut exemple pour montrer comment extraire les informations qui a été ajouté.

+1

Impressionnant - merci Adam. Voici une autre page: http://www.jarrettmeyer.com/2009/07/using-data-annotations-with-metadata.html Je me demande encore quelles sont les bibliothèques avec lesquelles vous pouvez utiliser MetadataType? Pour trouver les attributs définis dans une classe MetadataType, vous devez vraiment les rechercher, et il semble que ce ne sont pas toutes les bibliothèques .NET standard qui le feraient. Apparemment, MetadataType est utilisé avec ASP.NET - existe-t-il d'autres emplacements standard? Quoi qu'il en soit, merci encore. – shaunmartin

+0

Je ne suis pas sûr s'il y a des places standards ou non. La plus grande partie de la description de la classe elle-même est centrée sur l'utilisation du nouveau support d'objet de données (Entity Framework, LINQ to SQL, etc.). Cela aurait plus de sens car il permet l'ajout d'attributs de validation supplémentaires. –

3

J'avais une situation similaire. J'ai fini par écrire la méthode d'extension suivante pour cela. L'idée est de cacher l'abstraction de regarder à 2 endroits (classe principale et classe de métadonnées).

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

J'ai utilisé ceci, mais est passé de l'extension de PropertyInfo à l'extension MemberInfo - la ligne pi.DeclaringType.GetSingleAttribute n'a pas été compilée sans passer à MemberInfo. –

0

Ma solution pour un usage générique. Obtenez l'attribut la propriété que vous recherchez. Renvoie null si introuvable.

Si trouvé, il renvoie l'attribut lui-même. Donc, vous pouvez avoir accès aux propriétés à l'intérieur de l'attribut si vous voulez.

Espère cette aide.

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

Étant donné les classes suivantes:

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

Je avais besoin de voir si Key a été définie sur une propriété pour Person classe. J'ai ensuite eu besoin d'obtenir la valeur de la propriété. En utilisant réponse @AdamGrid, j'ai modifié le code comme ceci pour l'obtenir:

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."); 
} 
Questions connexes