Проблема сопоставления EF 4.1

У меня есть класс, который имеет отношения сам с собой:

public class Person
{
    public long ID { get; set; }
    public string Name { get; set; }

    public virtual Person Mother { get; set; }
    public virtual Person Father { get; set; } 
}

Когда EF 4.1 пытается сопоставить этот класс, я получаю следующую ошибку: «Невозможно определить основной конец ассоциации между типами «Model.Person» и «Model.person». Основной конец этой ассоциации должен быть явно настроен с помощью API-интерфейса Fluent для отношений или аннотаций данных».

Я уже безуспешно пробовал решение темы EF 4.1 - Model Relationships.

Как я могу это исправить?

Спасибо!


person UbirajaraMNJ    schedule 22.05.2011    source источник


Ответы (3)


Поскольку это, естественно, отношения «один ко многим» (у человека должен быть один отец и одна мать, но может быть много сыновей и дочерей), я бы смоделировал это следующим образом:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>()
        .HasRequired(p => p.Father)
        .WithMany();

    modelBuilder.Entity<Person>()
        .HasRequired(p => p.Mother)
        .WithMany();
}

Это создаст два необходимых внешних ключа в таблице базы данных с именами Mother_ID и Father_ID по соглашению.

Изменить

Если вы хотите иметь возможность создавать людей без Матери и Отца, вы можете сделать отношение необязательным вместо обязательного:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>()
        .HasOptional(p => p.Father)
        .WithMany();

    modelBuilder.Entity<Person>()
        .HasOptional(p => p.Mother)
        .WithMany();
}

Тогда внешние ключи в таблице базы данных могут быть нулевыми.

Если вам не нравятся имена столбцов по умолчанию для ваших внешних ключей (Mother_ID и Father_ID), вы можете настроить имена столбцов в своем отображении:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>()
        .HasOptional(p => p.Father)
        .WithMany()
        .Map(m => m.MapKey("FatherID"));

    modelBuilder.Entity<Person>()
        .HasOptional(p => p.Mother)
        .WithMany()
        .Map(m => m.MapKey("MotherID"));
}
person Slauma    schedule 22.05.2011
comment
Я думаю, что это также потребует отключения каскадного удаления, потому что с точки зрения базы данных один человек может быть назначен как отцом, так и матерью, что приведет к нескольким путям каскадного удаления. - person Ladislav Mrnka; 22.05.2011
comment
@Ladislav: Да, я тоже думал, что будет необходимо отключить каскадное удаление, но оказалось, что приведенное выше сопоставление фактически делает это по умолчанию. Похоже, соглашение, согласно которому отношения «требуемый-ко-многим» моделируются с помощью каскадного удаления, не применяется к самоссылающимся ассоциациям. - person Slauma; 22.05.2011
comment
Еще один сюрприз от соглашений по умолчанию. MS должны прилагать больше усилий для точного документирования того, как и когда они что-то делают. - person Ladislav Mrnka; 22.05.2011
comment
Привет, Слаума, но в этом случае мне всегда нужно будет иметь объекты отца и матери для создания нового человека, у меня могут быть случаи, когда объект человека не может иметь отца и мать, как я могу отобразить эту возможность? - person UbirajaraMNJ; 24.05.2011
comment
@UbirajaraMNJ: я добавил в свой ответ раздел быстрого редактирования. - person Slauma; 24.05.2011
comment
@UbirajaraMNJ: Если подумать, требуемое сопоставление даже не имеет смысла. Где-то должен быть корень генеалогического дерева (первое лицо, которое я вставляю в БД) и у этого корня не может быть отца и матери. Чтобы выполнить ограничения базы данных, я мог создать только круг, что даже не имеет смысла. Таким образом, необязательно - это правильный путь, да. - person Slauma; 24.05.2011
comment
Привет Slauma, Спасибо за помощь. Ваш ответ был идеальным. - person UbirajaraMNJ; 24.05.2011
comment
Те WithMany() мало что говорят об обратном свойстве. То есть: что, если бы у Person был List‹Person› Children? - person sports; 24.04.2013
comment
@sports: я думаю, вам действительно понадобятся две коллекции ChildrenAsMother и ChildrenAsFather или около того. Для данного человека всегда будет пусто, потому что человек может быть либо женщиной, либо мужчиной. Но проблема в том, что вы не можете сопоставить одну коллекцию Children как с Person.Mother, так и с Person.Father как обратное свойство. Children может быть только несопоставленным и доступным только для чтения свойством, которое представляет собой конкатенацию ChildrenAsMother и ChildrenAsFather. Не очень интуитивная модель, но я считаю, что с EF нет другого пути. WithMany будет WithMany(p => p.ChildrenAsMother/Father) - person Slauma; 24.04.2013
comment
@Slauma Было бы полезнее, если бы аннотации данных были проиллюстрированы таким же образом. - person Mark Macneil Bikeio; 13.03.2016

Для всех, кому интересно, это, по-видимому, невозможно сделать с помощью атрибутов DataAnnotation, а можно только свободно настроить. http://social.msdn.microsoft.com/Forums/en/adodotnetentityframework/thread/08bba96a-20b2-4a3c-9e0e-a5475b703dfe

person pwhe23    schedule 04.08.2012

Это немного странно, но вы можете сделать это без мусора modelBuilder, например:

public class RecursiveModel
{
    public long Id { get; set; }
    public string Name { get; set; }

    public RecursiveModel Father { get; set; }
    public RecursiveModel Mother { get; set; }
    public ICollection<RecursiveModel> Children { get; set; }
}

Таким образом, в основном одна из этих моделей будет иметь список своих дочерних элементов, и если, например, это мать, каждая из них будет иметь как Mother_Id, так и RecursiveModel_Id, установленные для этого объекта. Вам нужно будет явно подключать его каждый раз, в идеале с помощью вспомогательного метода, такого как:

    public void AddAsMotherOfChild(RecursiveModel child)
    {
        Children.Add(child);
        child.Mother = this;
    }

    public void AddAsFatherOfChild(RecursiveModel child)
    {
        Children.Add(child);
        child.Father = this;
    }

Не идеально, но он позволяет избежать кода modelBuilder, который, по крайней мере, для меня со временем становится хрупким и легионом.

person Chris Moschini    schedule 31.03.2013