Entity Framework e l'ereditarietà (Single Table Inheritance)

di Andrea Zani, in .NET,

Visto che in questi gioni sto usando parecchio con l'Entity Framework questa Table Per Hierarchy (Emanuele mi correggerà se, come al solito, sbaglio la nomenclatura), ecco due appunti su come si realizza con l'editor che mette a disposizione Visual Studio 2008.

L'inizio è questa semplice tabella dal nome BaseContents:

struttura tabella

Da questa singola tabella è possibile estrarre due entity distinte. Nel mio caso:

  • TextContents
  • LinkContents

La prima utilizzata per memorizzare il contenuto reale di un eventuale testo, la seconda per link a risorse esterne. All'interno di questa singola tabella sono contenuti tutti i campi per memorizzare queste informazioni. L'unico campo aggiuntivo, che farà da discriminante, come si vedrà di seguito, è Type.

Creato in Visual Studio 2008 un nuovo oggetto ADO.Net Entity Data Model, e importanto all'interno di questo oggetto la tabella sopra mostrata, si avrà la prima entity creata in automatico dall'ambiente di sviluppo con tutti i campi interessati.

Ora è possibile aggiunere le entity mancanti, TextContents e LinkContents, ereditandole direttamente da questa (è sufficiente cliccare con il tasto destro all'interno dell'editor e selezionare Add -> Entity). Aggiunte le due entity ecco cosa abbiamo in VS 2008:

entity

Ora possiamo spostare le property di nostro interesse nelle sottostanti entity per ottenere questo:

entity

Dalla window Mapping details, mappiamo la tabella e assicurati che tutti i campi interessati siamo mappati correttamente, possiamo inserire la condizione discriminante, in modo che nell'uso di una o dell'altra entity, siano salvati i dati corretti nella tabella. Nulla di difficile, sempre da Mapping details, si aggiunge nelle condition:

conditions

In questo caso ho inserito il valore 1 in Type per il TextContents, e 2 per il LinkContents. Sembra tutto fatto, ma ecco l'amara sorpresa che ho trovato compilando il tutto:

Error 1 Error 3032: Problem in Mapping Fragment starting at line 49: Condition member 'BaseContents.Type' with a condition other than 'IsNull=False' is mapped. Either remove the condition on BaseContents.Type or remove it from the mapping.

Scopro che Type crea un problema. Il mio primo tentativo di cancellazione di questa property dall'entity BaseContents mi porta un nuovo errore:

Error 1 Error 3023: Problem in Mapping Fragments starting at lines 48, 54, 59: Column BaseContents.Type has no default value and is not nullable. A column value is required to store entity data.
An Entity with Key (PK) will not round-trip when:
(PK is in 'BaseContents' EntitySet AND Entity is type TestEntitiesModel.BaseContents)

Alla fine scopro la soluzione da un blog di Zeeshan Hirani (che spiega il tutto in modo molto più dettagliato, ma visto che ho scritto fino a qua, non ho voglia di buttare il tutto) che ringrazio perché mi ha fatto trovare immediatamente la soluzione. E' sufficiente modificare la property Abstract dell'Entity BaseContents in true, per eliminare ogni errore.

Ora da codice possiamo inserire e leggere da questa tabella con due entity separate:

using (TestEntitiesModel.TestEntitiesEntities context = new TestEntitiesModel.TestEntitiesEntities())
{
TestEntitiesModel.TextContents c = new TestEntitiesModel.TextContents();
c.Name = "Example 1";
c.Content = "Content text bla bla bla";
context.AddToBaseContents(c);
TestEntitiesModel.LinkContents l = new TestEntitiesModel.LinkContents();
l.Name = "Example 2";
l.Link = "www.aspitalia.com";
l.Impressions = 0;
context.AddToBaseContents(l);
context.SaveChanges();
}

Visualizzando il contenuto della tabella troveremo:

Id Name Link Impressions Content Type
1 Example1 null null Content text bla bla bla 1
2 Example2 www.aspitalia.com 1 null 2

Per leggere:

using (TestEntitiesModel.TestEntitiesEntities context = new TestEntitiesModel.TestEntitiesEntities())
{
var coll = from c in context.BaseContents.OfType<TestEntitiesModel.TextContents>()
orderby c.Content ascending
select c;
foreach (var item in coll)
{
Response.Write(string.Format("{0} {1} {2}<br />",
item.Id,item.Name,item.Content));
}
}

Questa volta per specificare l'entity da leggere, dobbiamo utilizzare l'OfType come da esempio. Se volessimo richiedere l'entity LinkContents, avremmo duvuto scrivere:

var coll = from c in context.BaseContents.OfType<TestEntitiesModel.LinkContents>()

Oltre al Table Per Hierarchy (Single Table Inheritance) l'EntityFramework mette a disposizione altri due tipi di inheritance:

  1. Table Per Type
  2. Table Per Concrete Class

Ma le cosa iniziano a complicarsi un po' con l'editor di VS2008.

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Nella stessa categoria
I più letti del mese