Использование как «многие ко многим», так и «один ко многим» для одного и того же объекта

У меня есть ассоциация «многие ко многим» в EF Code-First (как описано в этот вопрос), и я также хочу использовать один ко многим для одного и того же объекта. Проблема в том, что EF не создает правильную схему базы данных. Код:

public class A
{
  public int Id { get; set; }
  public string Name { get; set; }
  public virtual ICollection<B> ObjectsOfB { get; set; }
}

public class B
{
  public int Id { get; set; }
  public virtual A ObjectA { get; set; }
  public virtual ICollection<A> OtherObjectsOfA { get; set; }
}

Когда я удаляю свойство ObjectA класса B, ассоциация "многие ко многим" создается правильно. При неправильном создании объект B получает 2 внешних ключа к A, а объект A получает 1 внешний ключ к B (как отношение «многие к одному»).


person Marthijn    schedule 22.11.2011    source источник


Ответы (2)


Если у вас есть более одного свойства навигации, ссылающегося на один и тот же объект, EF не знает, к чему принадлежит свойство обратной навигации в другом объекте. В вашем примере: A.ObjectsOfB относится к B.ObjectA или к B.OtherObjectsOfA? И то, и другое было бы возможным и допустимым образцом.

Теперь EF не выдает исключение типа «не может однозначно определить отношения» или что-то в этом роде. Вместо этого он решает, что B.ObjectA ссылается на третью конечную точку в B, которая не отображается как свойство навигации в модели. Это создает первый внешний ключ в таблице B. Два свойства навигации в B относятся к двум конечным точкам в A, которые также не представлены в модели: B.ObjectA создает второй внешний ключ в таблице B, а B.OtherObjectsOfA создает внешний ключ в таблице A.

Чтобы исправить это, вы должны явно указать отношения.

Первый вариант (самый простой) — использовать атрибут InverseProperty:

public class A
{
    public int Id { get; set; }
    public string Name { get; set; }
    [InverseProperty("OtherObjectsOfA")]
    public virtual ICollection<B> ObjectsOfB { get; set; }
}

Это определяет, что A.ObjectsOfB является частью отношения "многие ко многим" к B.OtherObjectsOfA.

Другой вариант — полностью определить отношения в Fluent API:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<A>()
        .HasMany(a => a.ObjectsOfB)
        .WithMany(b => b.OtherObjectsOfA)
        .Map(x =>
        {
            x.MapLeftKey("AId");
            x.MapRightKey("BId");
            x.ToTable("ABs");
        });

    modelBuilder.Entity<B>()
        .HasRequired(b => b.ObjectA)  // or HasOptional
        .WithMany()
        .WillCascadeOnDelete(false);  // not sure if necessary, you can try it
                                      // without if you want cascading delete
}
person Slauma    schedule 22.11.2011
comment
Спасибо за Ваш ответ! Однако для обоих решений я получаю следующее исключение: A first chance exception of type 'System.InvalidOperationException' occurred in EntityFramework.DLL - person Marthijn; 23.11.2011
comment
Исправление: беглое решение работает (моя ошибка ..), так что спасибо за это! Любая идея, почему InverseProperty выдает исключение? - person Marthijn; 23.11.2011
comment
@Henkie: Приложение действительно вылетает? Исключение первого шанса обычно не является проблемой и отображается только в окне вывода отладчика. Обычно его можно игнорировать. - person Slauma; 23.11.2011
comment
Приложение действительно не падает, но схема базы данных не генерируется. Я использую VS Express, поэтому я не могу сломаться при первых исключениях. - person Marthijn; 23.11.2011

Если таблица B имеет внешний ключ к таблице A, то класс B имеет свойство навигации к A, а A имеет свойство навигации к ICollection<A>.
Если таблица B имеет отношение многие ко многим с таблицей A, тогда класс A должен иметь ICollection<B>, а класс B должен иметь ICollection<A>.

Попробуйте, может быть, это прояснит ваш запрос от EF.

person Naor    schedule 22.11.2011
comment
Я забыл добавить ICollection в class A для ассоциации «многие ко многим» в моем примере кода. Теперь это исправлено. - person Marthijn; 22.11.2011
comment
@Henkie: Рад, что смог помочь :) - person Naor; 22.11.2011