2016-12-23 10 views
5

У меня проблема с свойствами навигации, в то время как я использую Inheritance (TPH - единственный доступный на данный момент в EF Core).Наследование и свойства навигации дочерним объектам

Мои модели иерархии:

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; } 

} 

И часть 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, давайте перейдем к делу. Поскольку вы можете видеть внутри родительской модели предложения, у меня есть свойство CreatorId - ссылка на сущность Employee. Внутри модель Сотрудника Я хочу иметь два свойства навигации для загрузки созданных Предложений типов детей, как показано ниже:

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

} 

Но это приводит к ошибкам миграции. После применения миграции у меня есть две ссылки на сущность Employee (CreatorId, EmployeeUserId) внутри таблицы предложений вместо одного (CreatorId). Когда я изменил навигационные свойства:

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

модель была правильной (там была только одна ссылки на Работник внутри Предложение таблицы), но я до сих пор не могу включать в себя() для модели CreatedProposalCustoms и CreatedProposalLeaves Employee отдельно.

Проблема, вероятно, в моей конфигурации DbContext, но я понятия не имею, как настроить его правильно:/

+1

Добавить 'public DbSet ProposalLeaves {get; задавать; } 'и' public DbSet ProposalCustoms {get; задавать; } 'в ваш класс« AppDbContext ». Затем вы можете создать свои свойства навигации в «Сотруднике». И сделайте их «виртуальными». – Kos

+0

Согласитесь с добавлением, но я бы не сделал их виртуальными, если вы на самом деле не нацелены на ленивую загрузку. – Zephire

+0

@ Kos Я применил ваши изменения и все еще не работает. '.Include (e => e.CreatedLeaves)' всегда содержит пустой список, а '_context.ProposalLeave.Where (pl => pl.CreatorId == emp.UserId) .ToList();' и '_context.Proposal.OfType (). Где (pl => pl.CreatorId == emp.UserId) .ToList() 'отлично работает:/О, и у меня все еще есть ненужный EmployeeUserId внутри таблицы предложений. – Kuba

ответ

3

Проблема заключается в том, что, когда вы должны навигационными свойствами, EF Ядро также создаст два внешних ключей, как вам уже выяснилось.

Одним из способов может быть non-mapped navigation properties, который просто завершает литье вашей коллекции базовым классом.

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>(); 
} 

Где два не отображенные свойства просто действовать как стенография для Proposals.OfType<T>()

Или, если вы хотите более общий характер:

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

затем использовать его в качестве employee.AllProposals<ProposalLeave>().Where(p => p.LeaveStart >= DateTime.Now).ToListAsync().

+0

Спасибо. Это не решило мою проблему вообще, но это был очень полезный намек;) – Kuba