Ho creato queste classi per generare il modello di database tramite l'approccio code-first EntityFramework 6:
public class Vehicle
{
public long Id { get; set; }
public long ResponsiblePersonId { get; set; }
}
public class Car: Vehicle {
public int HorsePower { get; set; }
}
public class Bike: Vehicle {
public int FrameSize { get; set; }
}
public class Organisation
{
public Organisation()
{
Cars = new List<Car>();
Bikes = new List<Bikes>();
}
public long Id { get; set; }
public List<Car> Cars { get; set; }
public List<Bike> Bikes { get; set; }
}
Finora questo mi è sembrato giusto. Sfortunatamente, la tabella risultante è simile alla seguente:
Id | ResponsiblePersonId | HorsePower | FrameSize | Discriminator | Organisation_Id | Organisation_Id1
Perché l'organizzazione ForeignKey viene generata due volte? Mi aspettavo che questa tabella avesse solo una colonna Organisation_Id.
Grazie
Esistono diversi modi per EF di implementare le tabelle fisiche per la gerarchia ereditaria. Quello predefinito, quello che stai usando, si chiama Table Per Hierarchy (TPH). Utilizza solo una tabella per tutte le entità derivate, con una colonna Discriminator
per specificare il tipo di entità contenuta nel record. EF aggiunge inoltre alla tabella una colonna per ogni proprietà inclusa in una qualsiasi delle entità derivate.
Pertanto, poiché la relazione tra le entità derivate e l' Organisation
viene definita a livello figlio (gli elenchi delle proprietà Car
e Bike
nell'entità Organisation
) EF decide di creare una colonna separata per ciascun tipo di entità figlio Organisation_Id
, e tu non lo desideri.
Come cambiarlo? Esistono diversi modi:
Non usare TPH. Utilizzare invece TPC (tabella per classe Concrete). Cioè, EF crea una tabella separata per ciascuna delle entità figlio. Come fare: rimuovere la proprietà DbSet<Vehicle>
da DbContext
. In caso contrario, imposta una configurazione esplicita per il nome della tabella fisica per ciascuna entità derivata da Vehicle
questo modo:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
...
modelBuilder.Entity<Car>().ToTable("Cars");
modelBuilder.Entity<Bike>().ToTable("Bikes");
}
Se è necessario continuare a utilizzare TPH, non conosco alcun modo per implementarlo che genererà solo una colonna OrganisationId
nel database e solo una chiave esterna tra Vehicle
e Organisation
. Il buon senso direbbe che potresti definire la chiave esterna Organisation
a livello di entità base del Vehicle
. Ma poi ricevi errori durante la generazione della migrazione:
Organizzazione: FromRole: NavigationProperty 'Organizzazione' non è valido. Digitare "Car" di FromRole "Organisation_Cars_Target" in AssociationType "Organisation_Cars" deve corrispondere esattamente al tipo "Veicolo" su cui è dichiarata la proprietà di navigazione.
Sembra che quando la relazione viene definita a livello di base, EF si aspetta che le liste Organisation
vengano definite di tipo Vehicle
e non Car
o Bike
. E questo non si adatta al tuo modello.
E se si tenta di definire le proprietà OrganisationId
o Organisation
nelle classi derivate, viene generato un errore diverso durante la generazione della migrazione, poiché non è consentito utilizzare lo stesso nome per le proprietà nelle diverse entità figlio. Puoi usare nomi diversi, ma poi ottieni nuovamente due colonne. Non c'è modo di ottenere neanche una colonna in questo modo.
Quindi, se rimani con TPH, per quanto ne so, devi sopportare di avere due colonne per il tuo OrganisationId
. Almeno puoi nominarli in modo più dettagliato con alcune configurazioni fluide:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
...
modelBuilder.Entity<Organisation>()
.HasMany(o => o.Bikes)
.WithRequired()
.Map(x => x.MapKey("OrganisationIdBike"));
modelBuilder.Entity<Organisation>()
.HasMany(o => o.Cars)
.WithRequired()
.Map(x => x.MapKey("OrganisationIdCar"));
}
Ti consiglierei di passare a TPC, poiché con il tuo modello le mappature fluide sono un po 'meno complesse da scrivere.
Per una migliore comprensione di TPH, TPC e TPT (tabella per tipo, ancora un'altra implementazione delle gerarchie di ereditarietà) leggi questo post .