2009-01-19 11 views
16

Desidero impostare un attributo su una proprietà pubblica in .NET, tuttavia non ho accesso alla proprietà esplicita stessa, poiché questo è stato generato in un altro file.Associare l'attributo con la proprietà generata dal codice in .net

ho questo campo:

public virtual string Name { get; set; } 

desidero impostare questo:

[ValidateNonEmpty("Name is required", ExecutionOrder = 1)] 
public virtual string Name { get; set; } 

La mia classe è contrassegnata come parziale, ma non si può avere proprietà parziali. Pensavo di fare qualcosa con la classe MetadataType che è una nuova funzionalità di Dynamic Data e DataAnnotations, ma purtroppo credo che possa essere utilizzata solo con Dynamic Data, è vero?

Citazioni: http://blogs.oosterkamp.nl/blogs/jowen/archive/2008/10/16/metadatatype-attribute.aspx http://blogs.msdn.com/davidebb/archive/2008/06/16/dynamic-data-and-the-associated-metadata-class.aspx

C'è un modo posso impostare questa attributi (anche attraverso web.config!) Senza toccare la classe codice generato?

Grazie in anticipo, Graham

risposta

22

Questo è un fastidio noto; semplicemente non è possibile aggiungere metadati ai membri generati.

Ci sono 6 opzioni qui (in ordine crescente di sforzo):

  • se si possiede l'attributo, permetterebbe di dichiararla contro la classe, per esempio: [ValidateNonEmpty("Name", "Name is required", ExecutionOrder = 1)] - quindi aggiungere più attributi al definizione classe parziale
  • utilizzare un metodo virtual/interface/etc per interrogare questo, piuttosto che tramite gli attributi
  • sublassare il tipo generato; sovrascrivere o dichiarare nuovamente il membro, aggiungendo i metadati (veramente disordinati)
  • utilizzare un valore personalizzato TypeDescriptionProvider per fornire metadati dinamici (molto, molto lavoro) - assumendo che il consumatore rispetti lo TypeDescriptor; maggior parte dei consumatori di legame legati fanno, ma per esempio, Expression (utilizzato da molti fornitori di LINQ) non
  • cambiamento del codice-generatore/scrivere il proprio
  • tenta di estendere qualcosa come PostSharp per fare il lavoro (Mi rifugio 't trovato un modo per fare questo, ma non ho l'amore di sentire se si trova un modo!)

io di solito hanno successo con la prima opzione, a meno che non si tratta di un attributo definito dal sistema ([DisplayName], ecc). Se [ValidateNonEmpty] è definito da dati dinamici, potrebbe non essere possibile farlo.

+0

Grazie Marc, ho pensato che questo potrebbe essere il caso. Sono riuscito a scorrere le proprietà della mia classe dichiarata "MetadataType", al punto in cui volevo informarti sugli attributi, e ho semplicemente confrontato il nome della proprietà "meta" con la proprietà reale. – GONeale

+0

Non è lo stesso che interrogare gli attributi veri, capisco, ma per quello che mi serve sembra che serva allo scopo in questo caso. Che è grandioso. – GONeale

+0

Speriamo che questo abbia senso. Ora posso vedere se un attributo di convalida è stato dichiarato e funziona di conseguenza. Ora spero solo che non ci sia un sovraccarico con me che utilizza la classe di attributi 'MetadataType' invece di crearne una che gli dica semplicemente su quale classe guardare le proprietà. – GONeale

1

Un'altra opzione è racchiudere le proprietà all'interno di proprietà non generate nella stessa classe. Non è l'ideale, perché potresti finire per avere proprietà doppie, ma se riesci a fare del tuo generatore proprietà protette sarebbe un buon approccio.

Appena dovuto affrontare questo problema: Entity Framework genera classi, voglio serializzarle su JSON con nomi più semplici.

// GENERATED BY EF 
public partial class ti_Users 
{ 
    public ti_Users() 
    { 
     this.ti_CardData = new HashSet<ti_CardData>(); 
     this.ti_Orders = new HashSet<ti_Orders>(); 
    } 

    protected int userId { get; set; } 
    protected string userName { get; set; } 
    protected string userEmail { get; set; } 
    protected string userPassHash { get; set; } 
    protected Nullable<System.DateTime> userLastLogin { get; set; } 
    protected string userLastIP { get; set; } 

    public virtual ICollection<ti_CardData> ti_CardData { get; set; } 
    public virtual ICollection<ti_Orders> ti_Orders { get; set; } 
} 

e l'add-on di classe:

[JsonObject(memberSerialization: MemberSerialization.OptIn)] 
public partial class ti_Users 
{ 
    [JsonProperty] 
    public int UserId 
    { 
     get { return this.userId; } 
     set { this.userId = value; } 
    } 

    [JsonProperty] 
    public string Name 
    { 
     get { return this.userName; } 
     set { this.userName = value; } 
    } 

    [JsonProperty] 
    public string Email 
    { 
     get { return this.userEmail; } 
     set { this.userEmail = value; } 
    } 

    [JsonProperty] 
    public string PassHash 
    { 
     get { return this.userPassHash; } 
     set { this.userPassHash = value; } 
    } 
} 
6

Dal momento che la classe generata è una classe parziale, il seguente dovrebbe funzionare:

  1. creare un'interfaccia che ha questa proprietà dichiarata in e decorarlo con l'attributo ValidateNonEmpty.
  2. Creare la propria classe parziale con lo stesso nome della classe AutoGenerated e rendere questo implementare l'interfaccia appena creata.
  3. La proprietà dovrebbe essere decorato con quell'attributo

Ad esempio:

// Decorate the properties with attributes as required 
public interface IMyInterface 
{ 
    [ValidateNonEmpty("Name is required")] 
    string Name { get; set; } 
} 

// This is the partial class I created, that implements the interface 
public partial class MyGeneratedClass : IMyInterface 
{  
} 

// This is the auto-generated class 
public partial class MyGeneratedClass 
{ 
    public virtual string Name { get; set; } 
} 

Ho avuto questa idea da geekswithblogs.

+0

Penso che poi avresti bisogno di ogni singola proprietà della tua classe base inserita nell'interfaccia, un esercizio noioso se hai molte classi generate e molte proprietà. – secretwep

2

Questa è un'ottima soluzione, ma non ha funzionato per il mio problema. Sto usando EF 6 con classi generate dal codice prima da un database esistente. Una delle colonne in una tabella è un'IDENTITÀ con valori generati automaticamente. Tuttavia, la classe parziale generata non ha fornito l'attributo [DatabaseGenerated (DatabaseGeneratedOption.Identity)] per consentire al database di generare la chiave. Il risultato è l'errore "Impossibile inserire il valore esplicito per la colonna Identity nella tabella 'mytable' quando IDENTITY_INSERT è impostato su OFF.". Ho provato la tua soluzione ma non ha funzionato. Ma se aggiungo l'attributo alla classe originale generata, funziona. Quindi sto ancora cercando di trovare una soluzione che non richieda la modifica del file generato automaticamente.

Ecco il codice Ho provato ad utilizzare la vostra soluzione:

public interface IMyTable 
{ 
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    int ID { get; set; } 
} 

public partial class MyTable : IMyTable 
{ 
} 

originale codice generato:

[Table("MyTable")] 
public partial class MyTable 
{ 
    [Key] 
    [Column(Order = 1)] 
    public int ID { get; set; } 
} 
Problemi correlati