子實體的繼承和導航屬性

asp.net-core c# ef-database-first entity-framework-core

我在使用繼承時遇到導航屬性問題(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");

    }
}

好的,讓我們談談。正如您在父Proposal模型中看到的,我有屬性CreatorId - 對Employee實體的引用。在Employee模型內部我希望有兩個導航屬性來加載創建的子類型的Proposals,如下所示:

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

}

但它會導致遷移錯誤。應用遷移後,我在Proposal表中有兩個對Employee實體(CreatorId,EmployeeUserId)的引用,而不是一個(CreatorId)。當我將導航屬性更改為:

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

模型是正確的(在Proposal表中只有一個Employee引用),但我仍然不能分別包含()到Employee模型CreatedProposalCustoms和CreatedProposalLeaves。

問題可能在我的DbContext配置中,但我不知道如何正確設置它:/

一般承認的答案

問題是,當您需要導航屬性時,EF Core還將創建兩個您已經找到的外鍵。

一種解決方法可能是使用非映射的導航屬性 ,它只使用基類包裝集合的轉換。

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



Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因