2015-05-23 11 views
15

Come posso mantenere gli oggetti valore in Entity Framework senza inquinare il mio modello di dominio? EF (beh, DB relazionali in genere) mi impongono di definire una chiave - che i miei oggetti di valore non hanno fuori dalla scatola, per esempioCome gestire gli oggetti valore in Entity Framework?

public class Tag : ValueObject<Tag> 
{ 
    private readonly string name; 

    public Tag(string name) 
    { 
     this.name = name; 
    } 

    public string Name { get { return this.name; }} 
} 

D'altra parte, non dovrei rispondere alle preoccupazioni di persistenza nel modello. Devo davvero creare un'altra classe che includa tutti i campi dell'oggetto valore più una proprietà chiave e quindi associarli tra loro? Preferirei di no.

C'è forse una soluzione più elegante?

+2

È possibile ereditare la classe e aggiungere la proprietà chiave alla classe derivata –

+0

Ci ho pensato, ma mi piacerebbe comunque finire con 2 classi per oggetto valore. –

risposta

9

Vaughn Vernon scrive su Persistere oggetti valore (pagina 248) nel suo eccellente libro Implementing Domain-Driven Design.

ORM e valore singolo Oggetti

L'idea di base è quella di memorizzare ciascuno degli attributi del valore in colonne separate della riga in cui è memorizzato sua entità controllante. Detto in altro modo, un singolo oggetto valore viene denormalizzato nella riga della sua entità padre. Ci sono vantaggi nell'impiegare le convenzioni per la denominazione delle colonne per identificare chiaramente e standardizzare il modo in cui gli oggetti serializzati sono denominati.

ORM e molti valori sostenuta da un'entità di database

Un approccio molto semplice da persistere una raccolta di istanze di valore con un ORM e un database relazionale è quello di trattare il tipo di valore come entità nel modello di dati. (...) Per ottenere questo possiamo usare uno Layer Supertype.

Esempio delimitata contesti in C# si possono trovare qui: https://github.com/VaughnVernon/IDDD_Samples_NET

+1

Sì, è quello che ho finito per fare. ValueObject dispone ora di una chiave intera e quindi di tutti gli oggetti valore derivati. Ciò equivale a n + 1 classi invece di 2n classi. La classe ValueObject fa parte del kernel condiviso, quindi non è direttamente correlata al modello. –

+1

a meno che non si tratti di un 1-N (o molti a molti), utilizzare il tipo complesso di framework di entità. – Marco

4

Attualmente sto lavorando attraverso alcune di queste stesse sfide. Non sono davvero un fan di aggiungere un ID alla tua classe di base ValueObject<T> poiché questo dà un Id a tutti gli oggetti valore se sono necessari o più come un oggetto valore per definizione non ha Id, è qualcosa che eredita quel tipo di base no più a lungo essere un oggetto di valore nel senso puro.

Prima di andare oltre, vorrei notare che un concetto chiave nella codifica di DDD è che non devi essere puro DDD ovunque, purché tu sappia quali sono le tue concessioni e i loro trade-off. Detto questo, il tuo approccio può sicuramente essere considerato valido, tuttavia credo che aggiunga una concessione che potrebbe non essere realmente necessaria. In primo luogo, questo influisce sull'uguaglianza dei tuoi oggetti valore. Con l'aggiunta di Id, due Tag, anche con lo stesso nome non sono più uguali.

Ecco i miei approcci a questa situazione: Prima il più semplice, non proprio applicabile a quello che penso sia il tuo problema ma è importante. Questo è l'oggetto a valore singolo nella prima parte della risposta di Martin.

  • Rendi l'oggetto valore una proprietà di un'entità.

Fintanto che l'oggetto valore è costituito da proprietà di tipo semplice, Entity Framework eseguirà il mapping corretto.

Per esempio:

public class BlogEntry : Entity<Guid> 
    { 
     public String Text { get; private set; } 
     public Tag Tag { get; private set; } 

     // Constructors, Factories, Methods, etc 
    } 

Entity Framework gestirà che bene, quello che finirà con è un singolo blogEntry tabella che consiste semplicemente di:

  • Id
  • Testo
  • Tag_Name

Ora immagino che in questo caso non sia quello che vuoi, ma per molti oggetti di valore funziona alla grande. Uno che uso frequentemente è un oggetto valore DateRange che consiste di diverse proprietà. Quindi sui miei oggetti di dominio ho semplicemente una proprietà del tipo DateRange. EF associa quelli alla tabella per l'oggetto dominio stesso.

Riporto questo perché torniamo alla concessione che abbiamo fatto di aggiungere Id al tipo di base ValueObject<T>, anche se Id potrebbe non essere elencato nell'implementazione concreta dell'oggetto dominio, è ancora lì e verrà comunque prelevato da Entity Framework che, per questo, probabilmente il caso d'uso degli oggetti value più comune non funziona più altrettanto bene.

OK, infine, sul tuo caso specifico (che anch'io ho incontrato alcune volte). Ecco come ho deciso di gestire la necessità che un'entità contenga un elenco di oggetti valore. Fondamentalmente si riduce ad ampliare la nostra comprensione del dominio. Supponendo che l'oggetto valore Tag sia per la registrazione di Tag in un post del blog, il modo in cui lo guardo è che un BlogPost contiene un elenco di PostTag con il valore di Tag. Sì, è un'altra classe, ma non è necessario aggiungerla per ogni oggetto valore, è necessaria solo quando si ha una lista di oggetti valore, e penso che esprima meglio ciò che sta accadendo.

Così qui è un esempio di aggiungere un elenco di un oggetto di valore ad un ente (usando il vostro oggetto valore di Tag sopra):

public class BlogEntry : Entity<Guid> 
    { 
     public String Text { get; private set; } 
     public ICollection<PostTag> PostTags { get; private set; } 

     // Constructors: 
     private BlogEntry(Guid id) : base(id) { } 
     protected BlogEntry() : this(Guid.NewGuid()) { } 

     // Factories: 
     public static BlogEntry Create (String text, ICollection<PostTag> tags = null) 
     { 
      if(tags == null) { tags = new List<PostTag>(); } 
      return new BlogEntry(){ Text = text, Tags = tags }; 
     }   

     // Methods: 
     public void AddTag(String name) 
     { 
      PostTags.Add(PostTag.Create(name)); 
     } 
    } 

    public class PostTag : Entity<Guid> 
    { 
     // Properties: 
     public Tag Tag { get; private set; } 
     public DateTime DateAdded { get; private set; } // Properties that aren't relevant to the value of Tag. 

     // Constructors: 
     private PostTag(Guid id) : base(id) { } 
     protected PostTag() : this(Guid.NewGuid()) { } 

     // Factories: 
     public static PostTag Create(Tag tag) 
     { 
      return new PostTag(){ Tag = tag, DateAdded = DateTime.Now }; 
     } 

     public static PostTag Create(Tag tag, DateTime dateAdded) 
     { 
      return new PostTag(){ Tag = tag, DateAdded = dateAdded }; 
     } 
    } 

che permettono al blogEntry di contenere più tag, senza compromettere gli oggetti di valore e Entity Framework lo mapperà perfettamente senza la necessità di fare nulla di speciale.

+0

Un'altra tecnica che ho apprezzato ultimamente con questo genere di cose è la memorizzazione della raccolta di oggetti valore come una stringa Json. L'oggetto entità che contiene la raccolta può gestire l'analisi in entrata e in uscita di una stringa in modo che il resto del modello continui a funzionare come una raccolta ma per l'archiviazione viene inserita come una stringa semplice. SQL Server può funzionare anche con i dati Json se è necessaria una query manuale, anche se è un po 'più di lavoro. Come bonus se le tue visualizzazioni utilizzano Ajax/Jquery puoi semplicemente usare la proprietà JsonString così com'è. –

Problemi correlati