Nello script #94 abbiamo visto come è possibile effettuare il mapping di una gerarchia di oggetti associandola per intero ad una sola tabella sul database. Questa strategia presenta però alcuni svantaggi:
1) Classi molto eterogenee producono tabelle con un gran numero di colonne le cui righe presentano molteplici valori a NULL
2) non è possibile inserire a livello di database vincoli di not-nullable sulle colonne specifiche di un tipo, nonostante magari a livello di business siano obbligatorie
3) Non è possibile forzare a livello di database (a meno di non usare soluzioni custom come trigger) le foreign key ad un particolare membro della gerarchia, dato che un vincolo di chiave esterna è soddisfatto da qualsiasi elemento della gerarchia stessa.
Un'alternativa, supportata da ADO.NET Entity Framework, è quello di utilizzare una specifica tabella per ogni classe concreta del modello a oggetti. Pertanto, lo stesso modello di dominio dello script #94 può essere persistito su uno schema simile al seguente:
Si noti che:
1) tutte le relazioni sono di tipo 1 a 1, dato che si tratta di vincoli di foreign key tra chiavi primarie (es. Automobili.AutomobileId e Veicoli.Id
2) per questo motivo, solo Veicoli.Id può essere di tipo Identity, mentre per gli altri è necessario poter specificare direttamente un valore
Importando uno schema di questo tipo in un object model, ADO.NET Entity Framework crea per default quattro entity e altrettante associazioni, che devono quindi essere eliminate manualmente e sostituite con le relazioni di ereditarietà; inoltre nelle classi derivate si possono eliminare anche le proprietà generate a partire dalle chiavi delle rispettive tabelle (quindi AutomobileId, CiclomotoreId e FurgoneId), visto che la chiave sarà esclusivamente la proprietà Id della classe base Veicolo:
Infatti, il successivo e ultimo passo è proprio quello di mappare quest?ultima proprietà su ognuna delle chiavi primarie delle singole tabelle, come mostrato in figura per la classe Automobile:
A questo punto ADO.NET Entity Framework possiede tutte le informazioni per persistere correttamente una gerarchia di entity di questo tipo. Infatti, ad esempio, il salvataggio di un nuovo Furgone
using (TestdbEntities context = new TestdbEntities()) { Furgone furgone = new Furgone(); // ...si valorizzano le proprietà... context.AddToVeicoli(furgone); context.SaveChanges(); }
produce tre query di INSERT sulle distinte tabelle Veicoli, Automobili e Furgoni:
INSERT [dbo].[Veicoli]([Proprietario], [NumeroAssicurazione]) VALUES (@0, @1) SELECT [Id] FROM [dbo].[Veicoli] WHERE @@ROWCOUNT > 0 AND [Id] = SCOPE_IDENTITY() INSERT [dbo].[Automobili]([AutomobileId], [AriaCondizionata], [Servosterzo], [NumeroAirbag]) VALUES (@0, @1, @2, @3) INSERT [dbo].[Furgoni]([FurgoneId], [CapacitàBagagliaio]) VALUES (@0, @1)
In un prossimo script, discuteremo le differenze e le implicazioni delle due strategie di mapping relativamente alla fase di fetch dei dati.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Supporto ai tipi DateOnly e TimeOnly in Entity Framework Core
Filtrare i dati di una QuickGrid in Blazor con una drop down list
Utilizzare il metodo CountBy di LINQ per semplificare raggruppamenti e i conteggi
Utilizzare QuickGrid di Blazor con Entity Framework
Change tracking e composition in Entity Framework
Utilizzare la funzione EF.Parameter per forzare la parametrizzazione di una costante con Entity Framework
Ottimizzare il mapping di liste di tipi semplici con Entity Framework Core
Utilizzare il metodo Index di LINQ per scorrere una lista sapendo anche l'indice dell'elemento
Persistere la ChatHistory di Semantic Kernel in ASP.NET Core Web API per GPT
Filtering sulle colonne in una QuickGrid di Blazor
Utilizzare EF.Constant per evitare la parametrizzazione di query SQL