EF Core TPH映射中派生类同名属性生成前缀列问题求助
EF Core TPH映射中派生类同名属性生成重复列的问题及解决办法
在EF Core的TPH(表继承)映射模式下,即使派生类中存在同名、同类型、同可空性的属性,EF Core仍会为这些属性生成带实体前缀的独立列(如示例中的ProjectNumber和Derived2_ProjectNumber),而非共享同一列。该问题在.NET 7(EF Core 7.0.14)和.NET 8版本中均存在。
代码复现
基类
public abstract class BaseClass { public int Id { get; set; } public string Name { get; set; } }
派生类1
public class Derived1 : BaseClass { public bool BoolFlagBla { get; set; } public string ProjectNumber { get; set; } }
派生类2
public class Derived2 : BaseClass { public int D1RandomProp { get; set; } public string ProjectNumber { get; set; } }
DbContext
public class BBContext : DbContext { private readonly string connectionString = "你的数据库连接字符串"; protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(connectionString) .EnableSensitiveDataLogging() .LogTo(str => Console.WriteLine(str)); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<BaseClass>().UseTphMappingStrategy(); } public DbSet<Derived1> DerivedSet1 { get; set; } public DbSet<Derived2> DerivedSet2 { get; set; } }
迁移生成结果
执行add-migration init后,迁移代码中生成了重复的列:
#nullable disable namespace TestTPH.Migrations { /// <inheritdoc /> public partial class init : Migration { /// <inheritdoc /> protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "BaseClass", columns: table => new { Id = table.Column<int>(type: "int", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Name = table.Column<string>(type: "nvarchar(max)", nullable: false), Discriminator = table.Column<string>(type: "nvarchar(max)", nullable: false), BoolFlagBla = table.Column<bool>(type: "bit", nullable: true), ProjectNumber= table.Column<string>(type: "nvarchar(max)", nullable: true), D1RandomProp = table.Column<int>(type: "int", nullable: true), Derived2_ProjectNumber = table.Column<string>(type: "nvarchar(max)", nullable: true) }, constraints: table => { table.PrimaryKey("PK_BaseClass", x => x.Id); }); } /// <inheritdoc /> protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( name: "BaseClass"); } } }
注意:ProjectNumber和Derived2_ProjectNumber为重复生成的列
原因分析
EF Core的TPH映射默认仅将基类中定义的属性视为所有派生类的共享属性,自动映射到同一列。而派生类各自定义的同名属性,即使特征完全一致,也会被EF Core判定为不同实体的独立属性,因此会生成带实体前缀的单独列。EF Core不会自动推断派生类的同名属性是共享字段,必须显式声明或配置。
解决办法
方案1:将共享属性移至基类(推荐)
这是最符合TPH设计思想的方案,将所有派生类的共享属性统一在基类中声明,派生类不再重复定义:
修改后的基类:
public abstract class BaseClass { public int Id { get; set; } public string Name { get; set; } public string ProjectNumber { get; set; } // 移至基类的共享属性 }
修改后的派生类1:
public class Derived1 : BaseClass { public bool BoolFlagBla { get; set; } // 移除重复的ProjectNumber }
修改后的派生类2:
public class Derived2 : BaseClass { public int D1RandomProp { get; set; } // 移除重复的ProjectNumber }
重新生成迁移后,EF Core会自动为ProjectNumber生成唯一列,所有派生类共用该列。
方案2:批量配置共享列(无法修改基类时使用)
如果无法修改基类结构,可以通过ModelBuilder批量配置所有派生类的同名属性映射到同一列,避免逐个手动配置:
在OnModelCreating中添加如下代码:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<BaseClass>().UseTphMappingStrategy(); // 批量处理同名属性映射 var sharedProperties = new[] { "ProjectNumber" }; // 这里可以添加所有需要共享的属性名 var derivedEntityTypes = modelBuilder.Model.GetEntityTypes() .Where(t => t.ClrType.IsSubclassOf(typeof(BaseClass)) && !t.ClrType.IsAbstract); foreach (var propName in sharedProperties) { foreach (var entityType in derivedEntityTypes) { if (entityType.FindProperty(propName) != null) { modelBuilder.Entity(entityType.ClrType) .Property(propName) .HasColumnName(propName); } } } }
这段代码会遍历所有继承自BaseClass的非抽象实体,将指定名称的属性统一映射到同名数据库列,覆盖EF Core默认的前缀命名规则。
内容的提问来源于stack exchange,提问作者MrDim
相关产品推荐
相关产品推荐

