You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用EntityFramework6在SQL Server存储子类列表的方案咨询

Entity Framework 6 中存储子类列表的可行方案

一、你首选的单表存储方案(TPH:表每层次)

完全可以实现你要的表结构 {ClassType NOT NULL, Id NOT NULL, Order NOT NULL, Quantity NULL, Price NULL},这是EF6默认支持的继承映射方式,所有继承层次的实体都存在同一张表,通过鉴别列区分不同子类。

实现步骤

  1. 调整模型字段可空性
    子类专属字段在其他类型实体中无值,需改为可空值类型,否则数据库会强制非空导致插入失败:

    public abstract class Base
    {
        public int Id { get; set; }
        public int Order { get; set; }
    }
    
    public class Specific1 : Base
    {
        public int? Quantity { get; set; } // 改为可空
    }
    
    public class Specific2 : Base
    {
        public int? Price { get; set; } // 改为可空
    }
    
    public class Owner
    {
        public int Id { get; set; }
        public ICollection<Base> Children { get; set; }
    }
    
  2. DbContext配置
    在OnModelCreating中配置鉴别列和表映射:

    public class MyDbContext : DbContext
    {
        public DbSet<Owner> Owners { get; set; }
        public DbSet<Base> Bases { get; set; }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            // 配置基类主键与必填字段
            var baseEntity = modelBuilder.Entity<Base>();
            baseEntity.HasKey(b => b.Id);
            baseEntity.Property(b => b.Order).IsRequired();
    
            // 配置鉴别列ClassType,指定不同子类对应的标识值
            baseEntity.Map<Specific1>(m => m.Requires("ClassType").HasValue("Specific1"));
            baseEntity.Map<Specific2>(m => m.Requires("ClassType").HasValue("Specific2"));
    
            // 显式配置子类字段可空(模型已改为可空时可省略,这里做确认)
            modelBuilder.Entity<Specific1>().Property(s => s.Quantity).IsOptional();
            modelBuilder.Entity<Specific2>().Property(s => s.Price).IsOptional();
    
            // 配置Owner与Base的关联关系
            modelBuilder.Entity<Owner>()
                .HasMany(o => o.Children)
                .WithRequired()
                .Map(m => m.MapKey("OwnerId"));
        }
    }
    

生成的表结构

执行迁移后,会生成符合你要求的单表:

  • ClassType: 非空,存储子类标识(如"Specific1")
  • Id: 主键,非空
  • Order: 非空
  • Quantity: 可空
  • Price: 可空

这种方案的优势是手动查询简单,直接通过ClassType过滤即可获取指定子类数据。


二、其他可行方案

1. 表每类型(TPT)

将基类和每个子类分别存放在不同表中,子类表通过主键关联基类表:

  • 基类表Base: Id, Order, OwnerId
  • 子类表Specific1: Id, Quantity(Id为外键关联Base表)
  • 子类表Specific2: Id, Price(Id为外键关联Base表)

配置示例:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Base>().ToTable("Base");
    modelBuilder.Entity<Specific1>().ToTable("Specific1");
    modelBuilder.Entity<Specific2>().ToTable("Specific2");
}

优点是表结构更规范,子类字段可设为非空;缺点是手动查询需要关联多表,相对繁琐。

2. 表每具体类(TPC)

每个子类单独存放在一张表,包含基类的所有字段,无基类表:

  • Specific1表: Id, Order, Quantity, OwnerId
  • Specific2表: Id, Order, Price, OwnerId

配置示例:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Specific1>().Map(m =>
    {
        m.MapInheritedProperties();
        m.ToTable("Specific1");
    });
    modelBuilder.Entity<Specific2>().Map(m =>
    {
        m.MapInheritedProperties();
        m.ToTable("Specific2");
    });
}

优点是查询单个子类时无需关联,性能较好;缺点是基类字段重复存储,手动查询不同子类需要切换表,维护成本高。


内容的提问来源于stack exchange,提问作者Anders Juul

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 15:52:50