如何对具有多个 Entity Framework 6 外键的实体应用级联删除?

How to apply cascade delete on entities which have several foreign keys with Entity Framework 6?

我有以下两个实体:

[Table("TimeZone")]
public class TimeZone
{
    [Required]
    [Column("Name"), Index("IX_Name", IsUnique = true)]
    [StringLength(50)]
    public string Name { get; set; }

    [Column("Description")]
    [StringLength(100)]
    public string Description { get; set; }

    [Column("IANAID"), Index("IX_IANAID", IsUnique = true)]
    [StringLength(50)]
    public string IANAId { get; set; }

    public ICollection<ApplicationUser> Users { get; set; }

    public TimeZone()
    {
        Users = new HashSet<ApplicationUser>();
    }
}

[Table("User")]
public class ApplicationUser : IdentityUser<string, IdentityUserLogin, ApplicationUserRole, IdentityUserClaim>
{ 
    // [...]

    [Column("TimeZoneID"), ForeignKey("TimeZone")]
    public string TimeZoneId { get; set; }
    [Column("RegionID"), ForeignKey("Region")]
    public string RegionId { get; set; }
    [Column("ManagerID"), ForeignKey("Manager")]
    public string ManagerId { get; set; }

    // One-to-One
    public virtual TimeZone TimeZone { get; set; }

    // One-to-One
    public virtual Region Region { get; set; }

    // One-to-Many
    public virtual ApplicationUser Manager { get; set; }

    // One-to-many
    public virtual ICollection<Appointment> Appointments { get; set; }

    // Many-to-many
    public virtual ICollection<OnCallGroup> OnCallGroups { get; set; }

    // One-to-many
    public virtual ICollection<ApplicationUser> Subordinates { get; set; }
}

当我从TimeZone table中删除一条在User table中使用的记录时,根据级联删除约定,相关记录应该被删除。

但是由于还有其他外键,我在 Entity Framework 中得到以下异常:

The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.User_dbo.TimeZone_TimeZoneID". The conflict occurred in database "APPLICATIONDB_753c3d2ad2634cbf8cb62b098cdc6043", table "dbo.User", column 'TimeZoneID'. The statement has been terminated.

即使在 EF6 Code First 上默认启用了级联删除,从 TimeZone 中删除一条记录并不会删除所有相关用户,这是否正常?

注意: 在我的 ApplicationDbContext 中,我覆盖了 OnModelCreating 方法:

    protected override void OnModelCreating(DbModelBuilder dbModelBuilder)
    {
        base.OnModelCreating(dbModelBuilder);

        dbModelBuilder.Conventions.Add<OneToManyCascadeDeleteConvention>();
        dbModelBuilder.Conventions.Add<ManyToManyCascadeDeleteConvention>();
        dbModelBuilder.Conventions.Remove<PluralizingTableNameConvention>();


        // [...]
    }

FK 属性 (TimeZoneId) CLR类型是string,允许null,没有[Required]属性应用到FK或导航 属性,从而使关系 可选 ,即 ApplicationUser 可以在没有 TimeZone 的情况下存在。并且默认情况下,EF 不会在可选关系上启用级联删除。

要打开级联删除,您需要通过在 TimeZoneId 或 [=16] 上应用 [Required] 属性来使关系成为 required =] 属性,或者使用 fluent API 以防你需要通过在 OnModelCreating override 中添加以下内容来保持可选:

modelBuilder.Entity<TimeZone>()
    .HasMany(e => e.Users)
    .WithOptional(e => e.TimeZone)
    .HasForeignKey(e => e.TimeZoneId)
    .WillCascadeOnDelete(); // <--

这同样适用于使用 string FK 属性的其他关系。

更新:其实第二个选项并不是一个真正的选项。即使它设置了级联删除,EF 也会以不同的方式处理可选关系上的级联删除 - 当主体实体被删除时,它通过将 FKs 设置为 null 而不是删除它们来解除相关记录的关联。所以你真的必须通过使用上述属性或将流利的 API WithOptional 更改为 WithRequired.

来建立所需的关系