Entity Framework 代码优先:循环或多级联路径
Entity Framework Code first: cycles or multiple cascade paths
我有一个预订 class,它有一个预订联系人 (Person
) 和一组通过联接链接的导航属性 (People
) table Person
中的另一组导航属性 (Bookings
)。如何为预订联系关系生成启用级联删除的 Booking
table?当我将它排除在流畅的 API 代码之外(启用级联删除的默认设置)时,我从迁移中收到以下错误消息:
Introducing FOREIGN KEY constraint
'FK_dbo.BookingPeople_dbo.People_PersonID' on table 'BookingPeople'
may cause cycles or multiple cascade paths. Specify ON DELETE NO
ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY
constraints.
Could not create constraint or index. See previous errors.
modelBuilder.Entity<Person>()
.HasMany<Booking>(s => s.aBookings)
.WithRequired(s => s.Contact)
.HasForeignKey(s => s.ContactId);
modelBuilder.Entity<Booking>()
.HasMany(t => t.People)
.WithMany(t => t.Bookings)
.Map(m => {
m.ToTable("BookingPeople");
m.MapLeftKey("BookingID");
m.MapRightKey("PersonID");
});
问题是您有多个级联删除路径,最终可能会尝试删除数据库中 BookingPeople
table 中的同一行。
您可以通过使用 Fluent API:
在一对多关系中禁用级联删除来避免这种不明确的删除路径
modelBuilder.Entity<Booking>()
.HasRequired(s => s.Contact)
.WithMany(s => s.aBookings)
.HasForeignKey(s => s.ContactId)
.WillCascadeOnDelete(false);
或者通过将关系定义为可选(使用可为空的外键,但您不能使用 Fluent Api 配置与级联删除的关系)。
modelBuilder.Entity<Booking>()
.HasOptional(s => s.Contact)
.WithMany(s => s.aBookings)
.HasForeignKey(s => s.ContactId);// ContactId is a nullable FK property
此外,您可以使用以下方法删除级联删除约定:
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
或者在多对多关系的情况下:
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
如果在删除Person
时需要删除所有与Person
关联的Bookings
,我的建议是将一对多关系配置为可选,并覆盖SaveChanges
方法:
public override int SaveChanges()
{
Bookings.Local
.Where(r => r.ContactId == null)
.ToList()
.ForEach(r => Bookings.Remove(r));
return base.SaveChanges();
}
如果依赖实体上的外键可以为空,Code First 不会在关系上设置级联删除,当主体被删除时,外键将设置为 null
。这样就可以找到SaveChanges
方法中的孤儿并删除
我有一个预订 class,它有一个预订联系人 (Person
) 和一组通过联接链接的导航属性 (People
) table Person
中的另一组导航属性 (Bookings
)。如何为预订联系关系生成启用级联删除的 Booking
table?当我将它排除在流畅的 API 代码之外(启用级联删除的默认设置)时,我从迁移中收到以下错误消息:
Introducing FOREIGN KEY constraint 'FK_dbo.BookingPeople_dbo.People_PersonID' on table 'BookingPeople' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint or index. See previous errors.
modelBuilder.Entity<Person>()
.HasMany<Booking>(s => s.aBookings)
.WithRequired(s => s.Contact)
.HasForeignKey(s => s.ContactId);
modelBuilder.Entity<Booking>()
.HasMany(t => t.People)
.WithMany(t => t.Bookings)
.Map(m => {
m.ToTable("BookingPeople");
m.MapLeftKey("BookingID");
m.MapRightKey("PersonID");
});
问题是您有多个级联删除路径,最终可能会尝试删除数据库中 BookingPeople
table 中的同一行。
您可以通过使用 Fluent API:
在一对多关系中禁用级联删除来避免这种不明确的删除路径 modelBuilder.Entity<Booking>()
.HasRequired(s => s.Contact)
.WithMany(s => s.aBookings)
.HasForeignKey(s => s.ContactId)
.WillCascadeOnDelete(false);
或者通过将关系定义为可选(使用可为空的外键,但您不能使用 Fluent Api 配置与级联删除的关系)。
modelBuilder.Entity<Booking>()
.HasOptional(s => s.Contact)
.WithMany(s => s.aBookings)
.HasForeignKey(s => s.ContactId);// ContactId is a nullable FK property
此外,您可以使用以下方法删除级联删除约定:
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
或者在多对多关系的情况下:
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
如果在删除Person
时需要删除所有与Person
关联的Bookings
,我的建议是将一对多关系配置为可选,并覆盖SaveChanges
方法:
public override int SaveChanges()
{
Bookings.Local
.Where(r => r.ContactId == null)
.ToList()
.ForEach(r => Bookings.Remove(r));
return base.SaveChanges();
}
如果依赖实体上的外键可以为空,Code First 不会在关系上设置级联删除,当主体被删除时,外键将设置为 null
。这样就可以找到SaveChanges
方法中的孤儿并删除