Подтвердить что ты не робот

Сопоставление внешнего ключа в функции HasOptional(). WithOptionalDependent() в Entity Framework 6

У меня есть следующая модель данных в Entity Framework 6.1.3:

using System.Data.Entity;

public class Student
{
    public int Id { get; set; }
    public virtual Contact Contact { get; set; }
}

public class Contact
{
    public int Id { get; set; }
    public virtual Student Student { get; set; }
}

public class MyContext : DbContext
{
    protected override void OnModelCreating(DbModelBuilder builder)
    {
        builder.Entity<Contact>()
            .HasOptional(x => x.Student)
            .WithOptionalDependent(x => x.Contact)
            .WillCascadeOnDelete(true);
    }
}

public static class Program
{
    private static void Main()
    {
        Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());

        using (var context = new MyContext())
            context.Database.Initialize(force: true);
    }
}

Когда я запускаю этот код, я получаю точно правильную структуру таблицы, на которую я нацелен:

dbo.Contacts
    Id (PK)
    Student_Id (FK, NULL, CASCADE ON DELETE)

dbo.Students
    Id (PK)

Однако теперь я хотел бы добавить свойство Student_Id, которое будет доступно в объекте Contact. Поэтому я могу прочитать Student_Id без необходимости вступать в другую таблицу через навигацию .Student.Id.

Если я добавлю свойство в объект Contact, я получаю либо два столбца Student_Id, и Student_Id1, либо я получаю сообщение об ошибке Each property name in a type must be unique..

Столбец уже находится в базе данных, все, что мне нужно, это иметь его в сущности, почему это так много проблем? Есть ли решение?

4b9b3361

Ответ 1

Мне удалось получить ответ от менеджера программы Entity Framework после запроса GitHub.

К сожалению, это ограничение EF6. Вы не можете иметь свойство внешнего ключа в отношениях "один к одному", если это не является также свойством первичного ключа. Это связано с тем, что EF6 не поддерживает альтернативные ключи/уникальные индексы, поэтому вы не можете обеспечить, чтобы свойство не первичного ключа было уникальным. Тот факт, что вы можете сделать это, когда свойство внешнего ключа не находится в сущности, - это немного причуда... но, очевидно, мы не будем удалять 😄.

Альтернативные ключи BTW (и, следовательно, этот сценарий) поддерживаются в EF Core.

– Роуэн Миллер @  https://github.com/aspnet/EntityFramework6/issues/159#issuecomment-274889438

Ответ 2

Если вы хотите объявить свойство FK в зависимом объекте в отношениях один к одному, я боюсь, что вы также должны использовать его как ПК. EF Code First требует, чтобы PK зависимого объекта также был FK отношения:

public class Contact
{
    [Key,ForeignKey("Student")]
    public int StudentId { get; set; }
    public virtual Student Student { get; set; }
}

Но я думаю, что это не то, что вы ищете. Итак, я думаю, у вас есть три варианта:

  • Вы сохраняете свою текущую конфигурацию отношений.
  • Создайте аутентичные отношения один к одному.
  • Создание отношения "один ко многим"

По моему опыту, последний из них наиболее приспособлен к тому, чего вы пытаетесь достичь (но это мое мнение). В этом случае вы можете работать с свойством Fk по своему усмотрению, только вам нужно изменить свойство навигации Contact на Student на коллекцию (или опустить это свойство nav и создать однонаправленное отношение):

public class Student
{
    public int Id { get; set; }
    public virtual ICollection<Contact> Contacts { get; set; }
}

Конфигурация будет таким образом:

 builder.Entity<Contact>()
        .HasOptional(x => x.Student)
        .WithMany(x => x.Contacts)
        .HasForeignKey(x => x.StudentId)
        .WillCascadeOnDelete(true);

Update

Четвертым вариантом может быть создание двух однонаправленных отношений:

 builder.Entity<Contact>()
        .HasOptional(x => x.Student)
        .WithMany()
        .HasForeignKey(x => x.StudentId)
        .WillCascadeOnDelete(true);

 builder.Entity<Student>()
        .HasOptional(x => x.Contact)
        .WithMany()
        .HasForeignKey(x => x.ContactId)
        .WillCascadeOnDelete(true);

Но этот параметр нарушает реальное соотношение между двумя таблицами.