Ho un problema con le proprietà di navigazione e l'ereditarietà.
Questo è il mio problema: ho una classe di base Person
e classi User
e Worker
che ereditano da Person
. A livello di database sto usando l'ereditarietà di una tabella o ereditarietà di tabelle per gerarchia (TPH). Quindi c'è un solo tavolo con una colonna discriminatore.
Sia User
che Worker
devono avere una relazione Company
, quindi mi piacerebbe definirlo nella classe Person
.
Definisco il mio modello in questo modo:
[Table("mydb.person")]
public abstract partial class Person
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long ID { get; set; }
public long? CompanyID { get; set; }
[ForeignKey("CompanyID")]
public virtual Company Company { get; set; }
...
}
public partial class User : Person
{
...
}
public partial class Worker : Person
{
....
}
[Table("mydb.company")]
public partial class Company
{
public Company()
{
this.People = new HashSet<Person>();
this.Users = new HashSet<User>();
this.Workers = new HashSet<Worker>();
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long ID { get; set; }
public virtual ICollection<Person> People { get; set; }
public virtual ICollection<User> Users { get; set; }
public virtual ICollection<Worker> Workers { get; set; }
...
}
Ora, quando provo a fare una query per ottenere l'utente e la società collegata, ad esempio:
dbSet.Where(u => u.Username == username).Include(x => x.Company).FirstOrDefault();
La query non riesce con questa eccezione:
Colonna "Extent1.Company_ID" nell'elenco dei campi sconosciuto
Se analizzo il risultato SQL sembra qualcosa del genere:
SELECT
1 AS `C1`,
@gp2 AS `C2`,
`Extent1`.`ID`,
`Extent1`.`CompanyID`,
`Extent1`.`Username`,
...
`Extent1`.`Company_ID`
FROM `person` AS `Extent1`
WHERE `Extent1`.`Discriminator` = @gp1
Include la colonna Company_ID
aggiuntiva, che non esiste.
Ho provato un po 'di cose, niente ha funzionato:
CompanyID
a Company_ID
-> genera un Column_ID1
in SQL e genera la stessa eccezione Users
e Workers
da Company
-> lancia un'eccezione dicendo che non sa come mappare entità User
e Company
: Impossibile determinare la fine principale di un'associazione tra i tipi "Models.User" e "Models.Company". La fine principale di questa associazione deve essere configurata in modo esplicito utilizzando l'API della relazione fluente o le annotazioni di dati.
Company
, genera la stessa eccezione di mappatura come sopra Al momento non ho idee "pulite". L'unica cosa che potrebbe funzionare è fare un po 'di trucco sporco, definire tutte le relazioni sulle classi figlie, e fare query separate e fondersi nella classe base se sono richiesti sia gli utenti che i lavoratori.
Hai qualche suggerimento?
Rimuovere le proprietà della raccolta Utenti e Lavoratori.
public virtual ICollection<User> Users { get; set; }
public virtual ICollection<Worker> Workers { get; set; }
Poiché la proprietà di navigazione della Società è definita su Persona, la proprietà di navigazione sul retro associata deve essere un ICollection of Person.
La raccolta Persone conterrà tutti i lavoratori e gli utenti associati. Le due proprietà aggiuntive Utenti e Lavoratori vengono interpretate come relazioni completamente nuove e poiché non si hanno proprietà corrispondenti e chiavi esterne su Utente o Worker EF lo genera virtualmente.
Rispondi al commento. Solo per motivi di formattazione come seconda risposta ;-)
Con carico impaziente se inizi con la Compagnia
var companies = db.Company.Include(p => p.People);
Otterrà sempre gli utenti e i lavoratori.
Se usi il caricamento avido a partire dalle persone.
var users = db.People.OfType<User>().Include(p => p.Company).ToList();
var companies = users.Select(p => p.Company).Distinct().ToList();
la proprietà di navigazione People delle tue aziende ha solo gli utenti.
Inoltre è possibile eseguire due istruzioni separate e la correzione del contesto del database riempirà automaticamente le proprietà di navigazione.
var company = db.Company.Where(p => p.ID > 100).ToList();
var copanyUsers = db.Company.Where(p => p.ID > 100)
.SelectMany(p => p.People).OfType<User>().ToList();