如何在EF6与EF Core中设置删除Division时不级联删除Worker?
实体关系定义
public class Company { public int Id { get; set; } public string CompanyName { get; set; } [InverseProperty("Round")] public ICollection<Division> Divisions { get; set; } } public class Industry { public int Id { get; set; } public string IndustryName { get; set; } [InverseProperty("Industry")] public ICollection<Division> Divisions { get; set; } } public class Division { [Column(Order = 1), Key, ForeignKey("Company")] public int CompanyId { get; set; } [Column(Order = 1), Key, ForeignKey("Industry")] public int IndustryId { get; set; } public string DivisionName { get; set; } public ICollection<Worker> Workers { get; set; } } public class Worker { [Key] public int WorkerId { get; set; } [Column(Order = 2), ForeignKey("Division")] public int CompanyId { get; set; } [Column(Order = 2), ForeignKey("Division")] public int IndustryId { get; set; } public string WorkerName { get; set; } }
需求:删除Division实体时,不能同时删除关联的Worker实体,下面分别给出EF6和EF Core的配置方案。
EF6实现方案
首先要明确:当前Worker的CompanyId和IndustryId是非可空的int类型,要是直接禁用级联删除,删除Division时数据库会因为外键约束报错。所以要么把这两个外键改成可空类型,要么删除Division前手动把关联Worker的外键设为null。
1. 调整Worker实体(可选,根据业务需求)
如果允许Worker不关联任何Division,把外键改成可空:
public class Worker { [Key] public int WorkerId { get; set; } [Column(Order = 2), ForeignKey("Division")] public int? CompanyId { get; set; } [Column(Order = 2), ForeignKey("Division")] public int? IndustryId { get; set; } public string WorkerName { get; set; } }
2. 用Fluent API配置级联行为
在你的DbContext重写OnModelCreating方法,添加以下配置:
protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Worker>() .HasRequired(w => w.Division) // 要是外键改成可空了,就换成HasOptional .WithMany(d => d.Workers) .HasForeignKey(w => new { w.CompanyId, w.IndustryId }) .WillCascadeOnDelete(false); // 禁用级联删除,相当于Restrict }
配置后,EF6生成的外键约束不会带级联删除逻辑:如果外键可空,删除Division时Worker会被保留且外键值变为null;如果外键非空,必须提前处理Worker的关联,否则数据库会报错。
EF Core实现方案
EF Core的级联行为配置更灵活,同样要先处理外键的可空性问题,再选合适的删除行为。
方案1:允许Worker外键为空(推荐)
先修改Worker实体的外键为可空类型,同时建议显式添加导航属性(方便EF Core识别关系):
public class Worker { [Key] public int WorkerId { get; set; } [Column(Order = 2), ForeignKey("Division")] public int? CompanyId { get; set; } [Column(Order = 2), ForeignKey("Division")] public int? IndustryId { get; set; } public string WorkerName { get; set; } public Division Division { get; set; } // 显式添加导航属性 }
然后在DbContext的OnModelCreating里配置删除行为:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Worker>() .HasOne(w => w.Division) .WithMany(d => d.Workers) .HasForeignKey(w => new { w.CompanyId, w.IndustryId }) .OnDelete(DeleteBehavior.SetNull); // 删除Division时自动把Worker的外键设为null }
方案2:Worker外键必须非空
如果业务要求Worker必须关联某个Division,那就配置DeleteBehavior.Restrict,但删除Division前必须手动把关联Worker分配到其他Division,不然数据库会抛外键约束异常:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Worker>() .HasOne(w => w.Division) .WithMany(d => d.Workers) .HasForeignKey(w => new { w.CompanyId, w.IndustryId }) .OnDelete(DeleteBehavior.Restrict); // 禁止级联删除,有未处理的关联Worker时删除Division会报错 }
简单总结:SetNull适合外键可空的场景,删除Division自动清空Worker的关联;Restrict则是完全禁止级联,靠数据库约束阻止违规删除。
内容的提问来源于stack exchange,提问作者Mateusz Kaleta

