EF Core继承实体ID映射与表结构优化问题咨询
问题解决方案
一、子类ID映射到父类ClassifierId的实现
在EF Core默认的TPH(单表继承)策略下,要让子类的ManualClassifierId/ToolId和父类的ClassifierId映射到同一个数据库列,得用Fluent API显式配置:
配置代码(DbContext中)
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 配置鉴别器,区分两种子类类型 modelBuilder.Entity<Classifier>() .HasDiscriminator<string>("Discriminator") .HasValue<ManualClassifier>("Manual") .HasValue<ToolClassifier>("Tool"); // 把ManualClassifier的ManualClassifierId绑定到ClassifierId列 modelBuilder.Entity<ManualClassifier>() .Property(m => m.ManualClassifierId) .HasColumnName("ClassifierId") .ValueGeneratedOnAdd(); // 把ToolClassifier的ToolId绑定到ClassifierId列 modelBuilder.Entity<ToolClassifier>() .Property(t => t.ToolId) .HasColumnName("ClassifierId") .ValueGeneratedOnAdd(); // 确认父类主键 modelBuilder.Entity<Classifier>() .HasKey(c => c.ClassifierId); }
不过说实话,这种方式纯属冗余——子类本来就继承了父类的ClassifierId,没必要再搞个自己的ID字段,完全是画蛇添足。
二、更优方案:删掉子类的独立ID,直接用父类的主键
这才是EF Core继承映射的标准玩法,子类不需要自己的主键,直接用父类的ClassifierId当唯一标识,既简洁又符合继承逻辑。
修改后的实体类
public abstract class Classifier { public int ClassifierId { get; set; } public string ClassifierName { get; set; } public DateTime DateAdded { get; set; } } public class ManualClassifier : Classifier { // 删掉ManualClassifierId,直接用父类的ClassifierId public string user_name { get; set; } public string userName { get; set; } public string firstName { get; set; } public string lastName { get; set; } public string email { get; set; } public string password { get; set; } } public class ToolClassifier : Classifier { // 删掉ToolId,直接用父类的ClassifierId public string ToolName { get; set; } }
简化版配置
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Classifier>() .HasDiscriminator<string>("Discriminator") .HasValue<ManualClassifier>("Manual") .HasValue<ToolClassifier>("Tool"); }
这时EF Core会自动生成一个Classifier表,包含父类的所有字段、鉴别器Discriminator,还有两个子类的专属字段,鉴别器用来标记这条记录属于哪种类型。
三、解决TPH的空字段问题:换用TPT或TPCC
如果嫌TPH的空字段看着闹心,可以换两种继承策略:
1. TPT(分表继承):每个类型一张表
父类Classifier单独一张表,每个子类各一张表,子类表只存自己的属性和父类的主键(作为外键关联)。
配置代码:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Classifier>().ToTable("Classifiers"); modelBuilder.Entity<ManualClassifier>().ToTable("ManualClassifiers"); modelBuilder.Entity<ToolClassifier>().ToTable("ToolClassifiers"); }
这样父类表存通用属性,子类表存专属属性,关联查询时EF Core会自动拼接,完全没有空字段。
2. TPCC(具体类分表):每个子类一张完整表
每个子类单独一张表,里面包含父类的所有属性加自己的属性,父类不会生成表。
配置代码:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<ManualClassifier>().ToTable("ManualClassifiers"); modelBuilder.Entity<ToolClassifier>().ToTable("ToolClassifiers"); }
这种方式每张表都是独立的,没有空字段,但父类的属性会在每个子类表中重复,适合子类属性差异极大的场景。
总结
- 要是非保留子类ID不可,就用第一种Fluent API配置,但真心不推荐,纯冗余。
- 最优选择就是删掉子类的独立ID,复用父类的
ClassifierId,配合TPH最简单高效。 - 介意空字段的话,TPT是平衡冗余和结构的最佳选择;子类差异大的话可以考虑TPCC。
内容的提问来源于stack exchange,提问作者Miguel Ferreira
相关产品推荐
相关产品推荐

