子实体的继承和导航属性
Inheritance and navigation properties to child entities
我在使用继承(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");
}
}
好了,进入正题。正如您在父提案模型中看到的那样,我有 属性 CreatorId - 对 Employee 实体的引用。在 Employee 模型中,我希望有两个导航属性来加载创建的子类型提案,如下所示:
public class Employee
{
public ICollection<ProposalCustom> CreatedProposalCustoms { get; set; }
public ICollection<ProposalLeave> CreatedProposalLeaves { get; set; }
}
但它会导致迁移错误。在我应用迁移后,我在提案 table 中有两个对 Employee 实体 (CreatorId、EmployeeUserId) 的引用,而不是一个 (CreatorId)。当我将导航属性更改为:
public class Employee
{
public ICollection<Proposal> CreatedProposals { get; set; }
}
模型是正确的(在 Proposal table 中只有一个对 Employee 的引用),但我仍然无法将 Include() 分别用于 Employee 模型 CreatedProposalCustoms 和 CreatedProposalLeaves。
问题可能出在我的 DbContext 配置中,但我不知道如何正确设置它:/
问题是,当您必须导航属性时,EF Core 还会创建两个外键,正如您已经发现的那样。
一个解决方法是 non-mapped navigation properties,它只是用基础 class.
包装你的集合的铸造
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>();
}
其中两个未映射的属性仅作为 shorthand 用于 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()
。
我在使用继承(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");
}
}
好了,进入正题。正如您在父提案模型中看到的那样,我有 属性 CreatorId - 对 Employee 实体的引用。在 Employee 模型中,我希望有两个导航属性来加载创建的子类型提案,如下所示:
public class Employee
{
public ICollection<ProposalCustom> CreatedProposalCustoms { get; set; }
public ICollection<ProposalLeave> CreatedProposalLeaves { get; set; }
}
但它会导致迁移错误。在我应用迁移后,我在提案 table 中有两个对 Employee 实体 (CreatorId、EmployeeUserId) 的引用,而不是一个 (CreatorId)。当我将导航属性更改为:
public class Employee
{
public ICollection<Proposal> CreatedProposals { get; set; }
}
模型是正确的(在 Proposal table 中只有一个对 Employee 的引用),但我仍然无法将 Include() 分别用于 Employee 模型 CreatedProposalCustoms 和 CreatedProposalLeaves。
问题可能出在我的 DbContext 配置中,但我不知道如何正确设置它:/
问题是,当您必须导航属性时,EF Core 还会创建两个外键,正如您已经发现的那样。
一个解决方法是 non-mapped navigation properties,它只是用基础 class.
包装你的集合的铸造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>();
}
其中两个未映射的属性仅作为 shorthand 用于 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()
。