Код Entity Framework сначала: циклы или несколько каскадных путей
У меня есть класс бронирования, у которого есть контакт бронирования (Person
) и набор навигационных свойств (People
), который связывает таблицу соединений с другим набором свойств навигации (Bookings
) в Person
, Как мне сгенерировать Booking
таблица с каскадным удалением включена для связи контактов бронирования? Когда я оставляю его в свободном коде API (настройка каскадного удаления по умолчанию включена), я получаю следующее сообщение об ошибке при миграции:
Введение ограничения FOREIGN KEY "FK_dbo.BookingPeople_dbo.People_PersonID" в таблицу "BookingPeople" может привести к возникновению циклов или нескольких каскадных путей. Укажите ON DELETE NO ACTION или ON UPDATE NO ACTION, либо измените другие ограничения FOREIGN KEY.
Не удалось создать ограничение или индекс. Смотрите предыдущие ошибки.
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");
});
1 ответ
Проблема в том, что у вас есть несколько путей каскадного удаления, которые могут закончиться попыткой удалить одну и ту же строку в BookingPeople
таблица в БД.
Вы можете избежать таких неоднозначных путей удаления, отключив каскадное удаление в отношении "один ко многим" с помощью 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>();
Если вам нужно удалить все Bookings
ассоциируется с Person
при его удалении мой совет - настроить отношение один ко многим как необязательное и переопределить SaveChanges
метод:
public override int SaveChanges()
{
Bookings.Local
.Where(r => r.ContactId == null)
.ToList()
.ForEach(r => Bookings.Remove(r));
return base.SaveChanges();
}
Если внешний ключ на зависимом объекте имеет значение NULL, Code First не устанавливает каскадное удаление в отношении, и при удалении принципала внешний ключ будет установлен на null
, Таким образом, вы можете найти сирот в SaveChanges
метод и удалить их