EF Core保存含继承自有实体时报类型不在同一层级跟踪错误
问题原因
这个报错由EF Core对从属实体(Owned Entity,即通过OwnsOne/OwnsMany配置的值对象)的继承映射默认规则导致:
- 配置
Case实体的ThirdParty、User导航时,使用了无泛型参数的OwnsOne方法,EF Core自动识别关联类型时,会因为ContactWithIdNumber继承自Contact,默认将基类Contact注册为两个导航对应的从属实体类型,不会自动注册派生类ContactWithIdNumber的继承层级。 - 保存
Case实例时,ThirdParty、User传入的是ContactWithIdNumber类型实例,EF Core变更追踪器会尝试将实例匹配到已注册的Case.ThirdParty#Contact类型条目,但由于没有显式配置ContactWithIdNumber属于Contact从属类型的派生类,EF Core判定两个类型不属于同一继承层级,直接抛出跟踪失败错误。
修复方案
根据业务场景二选一即可:
方案1:显式指定从属类型为派生类(最简方案)
如果Case的两个联系人属性固定使用ContactWithIdNumber类型,不需要兼容Contact基类实例,直接在OwnsOne配置时显式指定泛型类型为ContactWithIdNumber,明确告知EF Core两个导航对应的从属类型为派生类,不要自动推断基类映射:
public void Configure(EntityTypeBuilder<Case> config) { config.ToTable("cases", LegalContext.DEFAULT_SCHEMA); config.HasKey(o => o.Id); config.Ignore(o => o.DomainEvents); // 显式指定从属类型为ContactWithIdNumber config.OwnsOne<ContactWithIdNumber>(o => o.ThirdParty, tp => { // 按需配置属性列名,避免和User的同名字段冲突 tp.Property(p => p.Name).HasColumnName("ThirdParty_Name"); tp.Property(p => p.ID_Number).HasColumnName("ThirdParty_ID_Number"); // 其余Contact基类属性按相同规则配置 }); config.OwnsOne<ContactWithIdNumber>(o => o.User, u => { u.Property(p => p.Name).HasColumnName("User_Name"); u.Property(p => p.ID_Number).HasColumnName("User_ID_Number"); // 其余Contact基类属性按相同规则配置 }); }
如果项目中存在全局自动将继承ValueObject的类型配置为Owned的逻辑,需要同步调整规则,不要单独将基类Contact注册为从属实体,避免类型冲突。
方案2:显式配置从属类型继承鉴别器
如果业务中存在同一导航属性可能传入Contact基类或ContactWithIdNumber派生类实例的场景,需要在配置Owned类型时显式添加鉴别器,告知EF Core两个类型的继承关系:
public void Configure(EntityTypeBuilder<Case> config) { config.ToTable("cases", LegalContext.DEFAULT_SCHEMA); config.HasKey(o => o.Id); config.Ignore(o => o.DomainEvents); config.OwnsOne<Contact>(o => o.ThirdParty, tp => { // 配置鉴别器区分不同派生类型 tp.HasDiscriminator<string>("ContactType") .HasValue<Contact>("Base") .HasValue<ContactWithIdNumber>("WithIdNumber"); // 配置派生类特有属性 tp.Property<ContactWithIdNumber>(p => p.ID_Number).HasColumnName("ThirdParty_ID_Number"); }); config.OwnsOne<Contact>(o => o.User, u => { u.HasDiscriminator<string>("ContactType") .HasValue<Contact>("Base") .HasValue<ContactWithIdNumber>("WithIdNumber"); u.Property<ContactWithIdNumber>(p => p.ID_Number).HasColumnName("User_ID_Number"); }); }
配置完成后EF Core可以正确识别同一继承链下的派生类型实例,不会抛出类型不匹配的跟踪错误。
内容的提问来源于stack exchange,提问作者Makhele Sabata
相关产品推荐
相关产品推荐

