2010-09-23 23 views
55

Est-il possible que je peux avoir un fichier de code généré comme ceci:Puis-je définir des propriétés dans des classes partielles, puis les marquer avec des attributs dans une autre classe partielle?

public partial class A { 
public string a {get; set;} 
} 

puis dans un autre fichier:

public partial class A { 
[Attribute("etc")] 
public string a {get; set;} 
} 

Pour que je puisse avoir une classe générée à partir de la base de données et utiliser un fichier non-généré pour le marquer?

+0

Combien est « générée à partir de la base de données »? Seules les définitions de propriétés ou le code? – snemarch

+1

Réponse courte, non. Réponse longue, dup de http://stackoverflow.com/questions/456624/associate-attribute-with-code-generated-property-in-net. –

+0

@snemarch: définitions de propriétés uniquement, je prévois de faire tout autre code à la main. –

Répondre

24

Je l'ai vu quelque chose comme ça fait dans un article de Scott Guthrie (à la fin de celui-ci) - n'a pas essayé moi-même, cependant.
http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx

[MetadataType(typeof(Person_Validation))] 
public partial class Person 
{ 
    // Partial class compiled with code produced by VS designer 
} 

[Bind(Exclude="ID")] 
public class Person_Validation 
{ 
    [Required(ErrorMessage = "First Name Required")] 
    [StringLength(50, ErrorMessage = "Must be under 50 characters")] 
    public string FirstName { get; set; } 

    [Required(ErrorMessage = "Last Name Required")] 
    [StringLength(50, ErrorMessage = "Must be under 50 characters")] 
    public string LastName { get; set; } 

    [Required(ErrorMessage = "Age Required")] 
    [Range(0, 120, ErrorMessage = "Age must be between 0 and 120")] 
    public int Age { get; set; } 

    [Required(ErrorMessage = "Email Required")] 
    [Email(ErrorMessage = "Not a valid email")] 
    public string Email { get; set; } 
} 
+5

Cette réponse mérite d'être mentionnée, mais ce n'est pas une solution générale à la question posée par le PO. Les consommateurs des attributs doivent toujours savoir pour rechercher la classe de métadonnées - c'est-à-dire que ces attributs ne seront * pas * renvoyés par Attribute.GetCustomAttribute (...). (Heureusement pour de nombreux cas d'utilisation, les consommateurs sont écrits par MS et dans certaines situations cela fonctionnera.) –

+0

Cette solution ne résout PAS le problème. Pourquoi cherchons-nous à décorer les membres dans un autre fichier? Parce que la classe est OVERWRITTEN chaque fois que le concepteur s'exécute. Ainsi, votre attribut '[MetaDataType ...' sera effacé chaque fois que le concepteur s'exécute –

+0

@Desolator - L'idée est que vous ne mettez pas l'attribut 'MetadataType' dans le fichier généré par le concepteur, vous le mettez dans l'autre fichier où la classe partielle 'Person' est définie. –

0

Non en tant que tel; le compilateur se plaindra que le membre est défini en plusieurs parties. Cependant, comme l'utilisation d'attributs personnalisés est de nature réflexive, vous pouvez définir une classe "métadonnées" et l'utiliser pour contenir des décorateurs.

public class A 
{ 
    public string MyString; 
} 

public class AMeta 
{ 
    [TheAttribute("etc")] 
    public object MyString; 
} 

... 

var myA = new A(); 
var metaType = Type.GetType(myA.GetType().Name + "Meta"); 
var attributesOfMyString = metaType.GetMember("MyString").GetCustomAttributes(); 
+1

Combien de fois est-ce que l'acteur qui ajoute les attributs à ses propriétés est aussi la personne qui les consomme et saura donc chercher les cours magiques "Meta"? –

+0

Très souvent, selon mon expérience. Cela ne fonctionnerait pas pour un framework orienté aspect existant, mais si vous étiez en train de décorer votre domaine avec, disons, des attributs de validation personnalisés, vous êtes celui qui les cherche et pouvez définir où. Mon équipe a fait exactement cela sur l'un de nos projets. Le principal inconvénient ne cherche pas l'autre classe; c'est maintenir deux classes parallèles en se développant, l'une fonctionnelle, l'autre décorative. Ce serait également un problème dans les classes partielles, si vous étiez en mesure de définir des champs/propriétés partiels en premier lieu. – KeithS

53

Je sais c'est une vieille question, mais voici la solution que j'utilise pour de tels cas. C'est utile lorsque vous avez des classes générées automatiquement que vous voulez décorer avec des attributs. Disons que c'est la classe générée automatiquement:

public partial class UserProfile 
{ 
    public int UserId { get; set; } 
    public string UserName { get; set; } 
    public string Firstname { get; set; } 
    public string Lastname { get; set; } 
} 

Et disons, je voudrais ajouter un attribut pour spécifier que UserId est la clé. Je puis créer une classe partielle dans un autre fichier comme celui-ci:

[Table("UserProfile")] 
[MetadataType(typeof(UserProfileMetadata))] 
public partial class UserProfile 
{ 
    internal sealed class UserProfileMetadata 
    { 
     [Key] 
     [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)] 
     public int UserId { get; set; } 
    } 
} 
+1

A travaillé comme un charme! Je vous remercie!!! –

+0

Excellente solution. Je savais que vous pouviez décorer la classe partielle avec des attributs dans plusieurs fichiers et même ajouter des interfaces et une classe héritée à sa déclaration, mais je n'ai pas fait la connexion à l'attribut 'MetadataType'. Bien joué! – Pflugs

+0

Que faire si je veux ajouter un attribut au constructeur de 'UserProfile'? – Botis

1

Ceci est ma réponse
différents fichiers de classe ou vous pouvez combiner les métadonnées dans un même fichier, mais garder l'espace de noms du same..so qu'ils peuvent se voient évidemment. Gardez à l'esprit lorsque vous mettez à jour votre modèle comme ajouter plus de colonnes vous devez également mettre à jour la classe de projet.

--your model class 
public partial class A { 
    public string a {get; set;} 
} 

--your project class 
public class Ametadata { 
    [Attribute("etc")] 
    public string a {get; set;} 
} 


[MetadataType(typeof(Ametadata))] 
public partial class A 
{ 
} 
0

Vous devez définir une classe partielle pour votre classe A comme exemple ci-dessous

using System.ComponentModel.DataAnnotations; 

// your auto-generated partial class 
public partial class A 
{ 
    public string MyProp { get; set; } 
} 

[MetadataType(typeof(AMetaData))] 
public partial class A 
{ 

} 

public class AMetaData 
{ 
    [System.ComponentModel.DefaultValue(0)] 
    public string MyProp { get; set; } 
}