EF Core中使用TPT时如何忽略基类定义的外键?
问题描述
我定义了如下Entity Framework Core模型:
class User { public int ID; public ICollection<Foo> OwnedFoos; } [PrimaryKey(nameof(ID), nameof(OwnerID))] class Foo { public int ID; public int OwnerID; [ForeignKey("OwnerID")] public User Owner; } class DerivedFoo : Foo { public int DerivedProp; }
使用TPT(Table-Per-Type)策略生成的SQL伪代码如下:
TABLE Users COLUMN ID TABLE Foos COLUMN ID COLUMN OwnerID PRIMARY KEY (ID, OwnerID) FOREIGN KEY OwnerID -> User.ID ON CASCADE DELETE TABLE DerivedFoos COLUMN ID COLUMN OwnerID COLUMN DerivedProp FOREIGN KEY OwnerID -> Users.ID ON CASCADE DELETE FOREIGN KEY (ID, OwnerID) -> (Foos.ID, Foos.OwnerID) ON CASCADE DELETE
这在SQL Server中生成数据库时会报错,因为存在多条级联删除路径。我可以通过Fluent API移除派生类到基类的级联删除,但更希望修改架构,仅移除DerivedFoos表中指向Users的外键,保留如下结构:
TABLE Users COLUMN ID TABLE Foos COLUMN ID COLUMN OwnerID PRIMARY KEY (ID, OwnerID) FOREIGN KEY OwnerID -> User.ID ON CASCADE DELETE TABLE DerivedFoos COLUMN ID COLUMN OwnerID COLUMN DerivedProp FOREIGN KEY (ID, OwnerID) -> (Foos.ID, Foos.OwnerID) ON CASCADE DELETE
但尝试使用Ignore()移除该关系时,EF工具会报错,因为该关系定义在基类上。请问有没有无需手动干预即可实现目标架构的方法?
解决方案
可以通过EF Core的Fluent API配置,让DerivedFoos表仅保留指向Foos表的复合外键,不生成直接关联Users的外键。具体实现如下:
在你的DbContext的OnModelCreating方法中添加以下配置:
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 配置TPT表映射 modelBuilder.Entity<Foo>().ToTable("Foos"); modelBuilder.Entity<DerivedFoo>().ToTable("DerivedFoos"); // 配置基类Foo的复合主键 modelBuilder.Entity<Foo>().HasKey(f => new { f.ID, f.OwnerID }); // 配置基类Foo与User的关联,仅在Foos表生成级联删除的外键 modelBuilder.Entity<Foo>() .HasOne(f => f.Owner) .WithMany(u => u.OwnedFoos) .HasForeignKey(f => f.OwnerID) .OnDelete(DeleteBehavior.Cascade); // 移除DerivedFoo表中OwnerID指向User的直接外键约束 var derivedFooEntity = modelBuilder.Entity<DerivedFoo>(); var userForeignKey = derivedFooEntity .HasOne<User>() .WithMany() .HasForeignKey(df => df.OwnerID) .Metadata; // 标记该外键不生成数据库约束,并禁用级联删除 userForeignKey.DeleteBehavior = DeleteBehavior.NoAction; userForeignKey.IsUnique = false; // 确保DerivedFoo到Foo的复合外键配置正确,保留级联删除 modelBuilder.Entity<DerivedFoo>() .HasBaseType<Foo>() .HasForeignKey(df => new { df.ID, df.OwnerID }) .OnDelete(DeleteBehavior.Cascade); }
这段配置会告诉EF Core:
- 基类
Foo的OwnerID外键仅在Foos表生成,带级联删除; - 派生类
DerivedFoo的OwnerID作为普通列存在,不生成指向Users的外键; - 保留
DerivedFoos到Foos的复合外键及级联删除逻辑。
这样生成的数据库结构就完全符合需求,不会出现SQL Server的级联删除路径冲突问题。
内容的提问来源于stack exchange,提问作者Mike Burton
相关产品推荐
相关产品推荐

