EF Core TPT模式下强制基表与派生表保留同名字段的实现问题
EF Core TPT模式下基表与派生表保留同名字段的配置方案
问题描述
在EF Core的TPT(表每类型)模式下,需要基表tbl_Person和派生表tbl_Employee各自保留ChangedDate必填字段(后续仅可能修改Employee表)。当前配置存在问题:插入操作时EF Core生成的SQL未包含ChangedDate和Name1等必填字段,导致Employee表插入失败(Person表插入正常)。
SQL Schema
CREATE TABLE [dbo].[tbl_Person]( [PersonId] [bigint] IDENTITY(1,1) NOT NULL, [Name1] [nvarchar](35) NOT NULL, [ChangedDate] datetime NOT NULL ) CREATE TABLE [dbo].[tbl_Employee]( [PersonId] [bigint] NOT NULL, [ChangedDate] datetime NOT NULL )
EF模型
public abstract class TblPerson { public long PersonId { get; set; } public string Name1 { get; set; } public DateTime ChangedDate{ get; set; } } public class TblEmployee : TblPerson { public DateTime ChangedDate{ get; set; } }
原EF配置(存在问题)
public class TblEmployeeConfiguration : IEntityTypeConfiguration<TblEmployee> { public void Configure(EntityTypeBuilder<TblEmployee> builder) { builder.ToTable("tbl_Employee", "dbo"); builder.HasBaseType<TblPerson>(); builder.Property(x => x.PersonId).HasColumnName(@"PersonId").HasColumnType("bigint").IsRequired().ValueGeneratedOnAdd().UseIdentityColumn(); builder.Property(x => x.ChangedDate).HasColumnName(@"ChangedDate").HasColumnType("datetime").IsRequired(); } public class TblPersonConfiguration : IEntityTypeConfiguration<TblPerson> { public void Configure(EntityTypeBuilder<TblPerson> builder) { builder.ToTable("tbl_Person", "dbo"); builder.HasBaseType((Type)null); builder.Property(x => x.PersonId).HasColumnName(@"PersonId").HasColumnType("bigint").IsRequired().ValueGeneratedOnAdd().UseIdentityColumn(); builder.Property(x => x.ChangedDate).HasColumnName(@"ChangedDate").HasColumnType("datetime").IsRequired(); } }
生成的错误SQL
exec sp_executesql N'SET NOCOUNT ON; INSERT INTO [dbo].[tbl_Person] ([PersonId]) VALUES (@p24); ',N'@p24 bigint',@p24=6000196
期望生成的SQL
exec sp_executesql N'SET NOCOUNT ON; INSERT INTO [dbo].[tbl_Person] ([PersonId], [Name1], [ChangedDate]) VALUES (@p24, @p25, @p26); INSERT INTO [dbo].[tbl_Employee] ([PersonId], [ChangedDate]) VALUES (@p24, @p27); ',N'@p24 bigint,@p25 nvarchar(35),@p26 datetime,@p27 datetime',@p24=6000196,@p25='张三',@p26='2022-11-15 10:40:31.953',@p27='2022-11-15 10:40:31.953'
解决方案
问题根源在于:EF Core默认将派生类与基类的同名属性视为同一个属性的重写,不会分别映射到两张表;同时原配置遗漏了Name1字段的映射,且派生表PersonId的自增配置错误。以下是修正后的配置:
修正后的基类配置
public class TblPersonConfiguration : IEntityTypeConfiguration<TblPerson> { public void Configure(EntityTypeBuilder<TblPerson> builder) { builder.ToTable("tbl_Person", "dbo"); // 明确配置基类主键 builder.HasKey(x => x.PersonId); builder.Property(x => x.PersonId) .HasColumnName("PersonId") .HasColumnType("bigint") .IsRequired() .ValueGeneratedOnAdd() .UseIdentityColumn(); // 新增Name1字段的映射(原配置遗漏) builder.Property(x => x.Name1) .HasColumnName("Name1") .HasColumnType("nvarchar(35)") .IsRequired(); // 明确基表的ChangedDate字段归属 builder.Property(x => x.ChangedDate) .HasColumnName("ChangedDate") .HasColumnType("datetime") .IsRequired(); } }
修正后的派生类配置
public class TblEmployeeConfiguration : IEntityTypeConfiguration<TblEmployee> { public void Configure(EntityTypeBuilder<TblEmployee> builder) { builder.ToTable("tbl_Employee", "dbo"); builder.HasBaseType<TblPerson>(); // 派生表PersonId是基表主键的外键,无需自增配置 builder.HasKey(x => x.PersonId); builder.Property(x => x.PersonId) .HasColumnName("PersonId") .HasColumnType("bigint") .IsRequired(); // 显式配置派生表的ChangedDate为独立字段,与基类属性无关联 builder.Property(x => x.ChangedDate) .HasColumnName("ChangedDate") .HasColumnType("datetime") .IsRequired() // 通过HasField指定私有字段,强制EF将其视为独立属性(可选,但能增强区分度) .HasField("_employeeChangedDate"); } }
额外说明
如果上述配置仍无法区分两个ChangedDate属性,可以在DbContext的OnModelCreating方法中显式声明字段归属:
modelBuilder.Entity<TblEmployee>() .Property(x => x.ChangedDate) .Metadata.SetIsTableColumn(typeof(TblEmployee).GetProperty("ChangedDate"), "tbl_Employee");
这样配置后,EF Core会分别处理基表和派生表的ChangedDate字段,插入时会生成包含所有必填字段的SQL,确保操作正常执行。
内容的提问来源于stack exchange,提问作者ThomasG
相关产品推荐
相关产品推荐

