5

J'ai un problème avec les propriétés de navigation lorsque j'utilise Inheritance (TPH - le seul disponible pour l'instant dans EF Core).Propriétés de l'héritage et de la navigation aux entités enfants

Mes modèles de hiérarchie:

public class Proposal 
{ 
    [Key] 
    public int ProposalId { get; set; } 

    [Required, Column(TypeName = "text")] 
    public string Substantiation { get; set; } 

    [Required] 
    public int CreatorId { get; set; } 

    [ForeignKey("CreatorId")] 
    public Employee Creator { get; set; } 

} 

public class ProposalLeave : Proposal 
{ 
    [Required] 
    public DateTime LeaveStart { get; set; } 

    [Required] 
    public DateTime LeaveEnd { get; set; } 
} 

public class ProposalCustom : Proposal 
{ 
    [Required, StringLength(255)] 
    public string Name { get; set; } 

} 

et une partie de DbContext:

public class AppDbContext : IdentityDbContext<User, Role, int> 
{ 

    public DbSet<Employee> Employee { get; set; } 
    public DbSet<Proposal> Proposal { get; set; } 

    public AppDbContext(DbContextOptions<AppDbContext> options) 
     : base(options) 
    { 
    } 

    protected override void OnModelCreating(ModelBuilder modelBuilder) 
    { 
     base.OnModelCreating(modelBuilder); 

     modelBuilder.Entity<Proposal>() 
      .HasDiscriminator<string>("proposal_type") 
      .HasValue<Proposal>("proposal_base") 
      .HasValue<ProposalCustom>("proposal_custom") 
      .HasValue<ProposalLeave>("proposal_leave"); 

    } 
} 

Ok, passons au point. Comme vous pouvez le voir dans le modèle de proposition parent, j'ai la propriété CreatorId - référence à l'entité Employee. Modèle Inside Employee Je souhaite avoir deux propriétés de navigation pour charger les propositions de types enfants créées ci-dessous:

public class Employee 
{ 
    public ICollection<ProposalCustom> CreatedProposalCustoms { get; set; } 
    public ICollection<ProposalLeave> CreatedProposalLeaves { get; set; } 

} 

Mais cela provoque des erreurs de migration. Après avoir appliqué la migration, j'ai deux références à l'entité Employee (CreatorId, EmployeeUserId) dans la table Proposal au lieu d'une (CreatorId). Lorsque je l'ai changé les propriétés de navigation:

public class Employee 
{  
    public ICollection<Proposal> CreatedProposals { get; set; } 
} 
modèle

était correct (il n'y avait qu'une seule référence à l'employé dans la table de proposition), mais je ne peux toujours pas inclure() pour le modèle employé CreatedProposalCustoms et CreatedProposalLeaves séparément.

Le problème est probablement dans ma configuration DbContext, mais je ne sais pas comment configurer correctement:/

+1

Ajouter 'public DbSet ProposalLeaves {get; ensemble; } 'et' public DbSet ProposalCustoms {get; ensemble; } 'à votre classe' AppDbContext'. Ensuite, vous pouvez créer vos propriétés de navigation dans 'Employee '. Et rends-les "virtuels". – Kos

+0

Acceptez l'ajout, mais je ne les rendrais pas virtuels à moins que vous ne vouiez en réalité un chargement paresseux. – Zephire

+0

@Kos J'ai appliqué vos modifications et cela ne fonctionne toujours pas. '.Include (e => e.CreatedLeaves)' donne toujours une liste vide, alors que '_context.ProposalLeave.Where (pl => pl.CreatorId == emp.UserId) .ToList();' et '_context.Proposal.OfType (). Où (pl => pl.CreatorId == emp.UserId) .ToList() 'fonctionne bien:/Oh, et j'ai toujours UserUserId inutile dans la table de proposition. – Kuba

Répondre

3

Le problème est que lorsque vous avez des propriétés de navigation, EF Core crée également deux clés étrangères que vous déjà trouvé.

Une solution de contournement pourrait être d'avoir non-mapped navigation properties, qui vient envelopper le casting de votre collection avec la classe de base.

public class Employee 
{ 
    public IDbSet<Proposal> Proposals { get; set; } 
    [NotMapped] 
    public IQueryable<ProposalCustom> CreatedProposalCustoms { get; } => Proposals.OfType<ProposalCustom>(); 
    [NotMapped] 
    public IQueryable<ProposalLeave> CreatedProposalLeaves { get; } => Proposals.OfType<ProposalLeave>(); 
} 

Lorsque les deux propriétés non-cartographiée serait tout simplement agir comme raccourci pour Proposals.OfType<T>()

Ou si vous le voulez plus générique:

public class Employee 
{ 
    public IDbSet<Proposal> Proposals { get; set; } 
    public IQueryable<T> AllProposals<T>() where T :Proposal => Proposals.OfType<T>(); 
} 

utiliser ensuite comme employee.AllProposals<ProposalLeave>().Where(p => p.LeaveStart >= DateTime.Now).ToListAsync().

+0

Merci. Ça n'a pas du tout résolu mon problème, mais c'était vraiment utile;) – Kuba