EF 4.1 сначала код: как ссылаться на дочерние записи от родительского элемента в отношениях «один ко многим»

#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. Как вы могли бы заметить, мой фактический код был более сложным, чем код, который я опубликовал, и после нескольких настроек я заставил ваше решение работать. Большое спасибо.