如何对具有多个 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
.
来建立所需的关系
我有以下两个实体:
[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
.