#entity-framework #foreign-keys #code-first
#entity-framework #внешние ключи #сначала код
Вопрос:
У меня есть простое совокупное отношение 1: много, скажем:
public class Parent
{
public string Name {get; set;}
public Child SelectedChild {get; set;}
public Child PublishedChild {get; set;}
public virtual ICollection<Child> AllChildren {get; set;}
}
public class Child
{
public string Name {get; set;}
[Required]
public Parent Father {get; set;}
}
При создании схемы из этой модели я получаю сообщение об ошибке:
Введение ограничения ВНЕШНЕГО КЛЮЧА ‘Parent_SelectedChild’ в таблицу ‘Parent’ может вызвать циклы или несколько каскадных путей. Укажите ПРИ УДАЛЕНИИ НИКАКИХ ДЕЙСТВИЙ или ПРИ ОБНОВЛЕНИИ НИКАКИХ ДЕЙСТВИЙ или измените другие ограничения ВНЕШНЕГО КЛЮЧА
Итак, я добавляю следующее в OnModelCreating:
modelBuilder.Entity<Child>()
.HasRequired(v => v.Parent)
.WithOptional(c => c.SelectedChild)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Child>()
.HasRequired(v => v.Parent)
.WithOptional(c => c.PublishedChild)
.WillCascadeOnDelete(false);
Это устраняет исходную ошибку, но теперь я получаю:
Не удалось определить основной конец отношения ‘xxx.Parent_SelectedChild’. Несколько добавленных объектов могут иметь один и тот же первичный ключ.
Кто-нибудь может помочь? Все, что я, по сути, хочу сделать, это ссылаться на конкретные дочерние записи в совокупном отношении 1: many от родительского элемента. Я предполагаю, что EF создаст столбцы идентификаторов дочерних элементов INT для родительского элемента, вызываемого, например, SelectedChild_Id и PublishedChild_Id (или аналогичные).
Заранее спасибо -macon
Редактировать: В ответ на @Slauma: я могу получить схему, сгенерированную с помощью:
modelBuilder.Entity<Parent>()
.HasOptional(p => p.SelectedChild)
.WithOptionalPrincipal()
.WillCascadeOnDelete(false);
modelBuilder.Entity<Parent>()
.HasOptional(p => p.PublishedChild)
.WithOptionalPrincipal()
.WillCascadeOnDelete(false);
modelBuilder.Entity<Parent>()
.HasMany(p => p.AllChildren)
.WithRequired(c => c.Father)
.WillCascadeOnDelete(false);
Но это генерирует несколько FK для дочерней записи, например, Parent_Id, Parent_Id1. Я просто хочу ссылку от родительского элемента на одну из дочерних строк, например Parent_SelectedChildId . Должен ли я делать это вручную с помощью столбца int в родительском элементе?
Ответ №1:
Я думаю, что у вас есть три отношения «1 ко многим»:
modelBuilder.Entity<Parent>()
.HasOptional(p => p.SelectedChild)
.WithMany()
.WillCascadeOnDelete(false);
modelBuilder.Entity<Parent>()
.HasOptional(p => p.PublishedChild)
.WithMany()
.WillCascadeOnDelete(false);
modelBuilder.Entity<Parent>()
.HasMany(p => p.AllChildren)
.WithRequired(c => c.Father)
.WillCascadeOnDelete(false);
Редактировать
Я протестировал свое приведенное выше сопоставление именно с тем Parent
Child
классом and, который вы указали в своем вопросе, — за единственным исключением, что я добавил свойство первичного ключа к обоим классам: public int Id { get; set; }
. В противном случае EF будет жаловаться на отсутствие ключевого свойства. Это сопоставление не создает исключения и создает в базе данных следующие таблицы:
Родительская таблица:
- Id int not nullable (PK)
- Name nvarchar(MAX) nullable
- SelectedChild_Id int nullable (FK)
- PublishedChild_Id int nullable (FK)
Детский стол:
- Id int not nullable (PK)
- Name nvarchar(MAX) nullable
- Father_Id int not nullable (FK)
Итак, как и ожидалось, есть три столбца внешнего ключа.
Поскольку вы получаете исключение в соответствии с вашим комментарием, я предполагаю, что на самом деле в тестируемом вами коде есть какое-то важное различие.
КСТАТИ: сопоставление двух навигационных свойств Parent
класса как отношений «один к одному» намного сложнее, если не невозможно. В EF вам нужен общий первичный ключ между двумя таблицами для сопоставления отношений «один к одному», поэтому было бы невозможно назначить две разные сущности двум свойствам навигации, поскольку они не могут иметь один и тот же ключ, что и родительский.
Комментарии:
1. Спасибо за ответ. Но это не работает. Я просто получаю: во время генерации модели была обнаружена одна или несколько ошибок проверки: System.Data.Edm.EdmAssociationType: : Multiplicity конфликтует со ссылочным ограничением в роли ‘Parent_SelectedChild_Target’ в отношении ‘Parent_SelectedChild’. Поскольку все свойства в зависимой роли не обнуляются, кратность главной роли должна быть ‘1’.
2. @macon: я добавил раздел редактирования к своему ответу выше.
3. Как вы могли бы заметить, мой фактический код был более сложным, чем код, который я опубликовал, и после нескольких настроек я заставил ваше решение работать. Большое спасибо.