DarioSantarelli.Blog("UgiDotNet");

<sharing mode=”On” users=”*” />
posts - 176, comments - 105, trackbacks - 3

My Links

News


This is my personal blog. These postings are provided "AS IS" with no warranties, and confer no rights.




Tag Cloud

Archives

Post Categories

My English Blog

[Entity Framework] Ereditarietà "Table-per-Hierarchy"

I modelli concettuali che possiamo definire tramite l' Entity Framework supportano 3 tipi diversi di ereditarietà:

  • Table-per-Hierarchy (TPH): Una tabella del database contiene i dati di tutti i tipi della gerarchia, lasciando ad una colonna il compito di fungere da discriminatore.
  • Table-per-Type (TPT):  Una tabella contiene le proprietà dell'Entity base. Ciascun sottotipo mappa quindi le sue specifiche proprietà su una tabella distinta.
  • Table-per-Concrete Class (TPC): Ad ogni tipo della gerarchia viene assegnata una tabella che ne mappa tutte le proprietà, incluse quelle ereditate.

Di tutti questi modelli di ereditarietà, il più semplice e facile da implementare è sicuramente il Table-per-Hierarchy (conosciuto anche come "Single Table Inheritance"), che tra l'altro è anche supportato da LINQ to SQL, a differenza degli altri due. Come è facile intuire, questo approccio in realtà non è poi così "pulito" :

  • Le informazioni relative a tutti i tipi della gerarchia vengono memorizzate in un'unica tabella e per ottenere la flessibilità necessaria si è costretti ad impostare le colonne di ciascun sottotipo come "allow nulls".
  • Di conseguenza, poiché la tabella risultante possiederà valori NULL in corrispondenza di colonne non utilizzate nel mapping verso le proprietà di ciascun sottotipo, è possibile che questa soluzione dia vita ad un inutile spreco di spazio su disco (dico "possibile" perché ormai i DB engine moderni gestiscono i valori NULL con tecniche sempre più ottimizzate - es. Sparse Columns in SQL Server 2008).

Ad ogni modo, allontaniamoci un attimo dalle considerazioni di carattere prestazionale e concentriamoci su un semplice esempio di implementazione di uno scenario TPH utilizzando l'EF. Prendiamo come esempio un modello concettuale relativo ad un embrionale RPG (Role-playing game):

  • Le Entities di tipo Warrior, Priest e Wizard sono derivate dell'Entity padre Character e costituiscono le uniche classi concrete del sistema.
  • L'Entity padre Character è astratta, ovvero rappresentata da una classe base non istanziabile direttamente. Per soddisfare un requisito del genere, basta impostare a true l'attributo Abstract dell' Entity (si può fare comodamente da design).

Nel DB abbiamo invece una singola tabella Characters a cui verrà affidato il compito di memorizzare le informazioni di tutti i tipi della gerarchia. In particolare, la colonna TypeID funge da discriminatore per i vari tipi di Character in quanto conterrà il codice identificativo del CharacterType corrispondente (es. TypeID = 1 -> Warrior).

Ora non rimane altro che impostare i table mappings. Vediamo rispettivamente quelli dell'Entity base Character e di un suo sottotipo (Warrior):

 
Il menu "Map Entity to functions" è giustamente disabilitato :)
 
1. Il model designer si comporta in modo corretto 
filtrando automaticamente le colonne mappabili in base alla
gerarchia di tipi definita nel modello concettuale.
2. La condition "When TypeID = 1" rappresenta il nostro
discriminatore per Entità di tipo Warrior.

N.B.: In questa fase bisogna evidenziare il fatto che, in caso di errori di mappings, il compile-time spesso fornisce messaggi non molto chiari che complicano il troubleshooting specialmente per chi in generale non ha esperienza nel mondo degli ORM.

OK It's code time!
Una volta che la generazione del modello è andata a buon fine, il designer autogenera l'intera infrastruttura per le solite operazioni CRUD a cui possiamo accedere tramite LINQ to Entities. Lo sviluppatore a questo punto può scrivere codice che sfrutta realmente le potenzialità e le best practices architetturali del mondo OO:

using (RPG_Entities rpgEntities = new RPG_Entities())
{
 
Warrior NewWarrior = new Warrior() { Name = "Ken Shiro" };
 
Priest NewPriest = new Priest() { Name = "Frate Tac" };
 
Wizard NewWizard = new Wizard() { Name = "Merlino" };                 

 
// Un unico metodo AddToCharacters che gestisce una qualunque sub-Entity della gerarchia
 
rpgEntities.AddToCharacters(NewWarrior);
 
rpgEntities.AddToCharacters(NewPriest);
 
rpgEntities.AddToCharacters(NewWizard);

 
rpgEntities.SaveChanges();
}

oppure:

using (RPG_Entities rpgEntities = new RPG_Entities())

  // Fetch in base all' Entity Type
  var warriors = rpgEntities.Characters.OfType<Warrior>();
  foreach (Warrior warrior in warriors)
  {
   // Do something...
  }
}


Per maggiori informazioni sull' ereditarietà di tipo TPH consiglio la lettura di questi articoli MSDN:


Technorati tags:  Entity Framework,  Ereditarietà

Print | posted on domenica 24 agosto 2008 17:30 |

Feedback

Gravatar

# Re: [Entity Framework] Ereditarietà "Table-per-Hierarchy"

le tue considerazioni, dal punto di vista puramente concettuale, sono ineccepibili.
vorrei solo precisare che l'uso o meno di una delle tre strategie cmq va valutato di volta in volta, vuoi per questioni di database legacy (lo schema è già presente e non è modificabile), vuoi per questioni di prestazioni, nel senso che se da una parte è vero che la TPH in qualche modo spreca spazio (difetto che oggi come oggi non arreca granchè disturbo), dall'altra evita di eseguire query con delle join, come nel caso delle altre due strategie.

saluti

saluti
Roberto
Gravatar

# Re: [Entity Framework] Ereditarietà "Table-per-Hierarchy"

il discorso a mio avviso va basato sullo scenario d'uso della tabella: ovvero se sarà una tabella pesantemente interrogata ed è mission critical, allora la TPC mi sta anche bene perchè evito di fare join, la duplicazione delle informazioni in questo caso non è un problema a confronto del requisito (e cmq in commercio ci sono dischi belli grossi, in assoluto il problema dello spazio non lo sento come tale).
fra l'altro la TPT, che è la più elegante se vogliamo, secondo me ha veramente senso se la classe base ha un suo perchè di business e non si limita ad essere una classe meramente astratta che fa da radice a classi derivate effettivamente utilizzate.
invece la TPH la vedo bene solo quando il campo discriminante funziona come una sorta di switch fra entità pressochè uguali le une alle altre, ovvero senza un eccessivo spreco di campi null.
tutte queste considerazioni partendo dal presupposto che il db vada progettato. se il db già esiste c'è ben poco da scegliere...

saluti
Roberto
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET