32

У меня есть два объекта в моем приложении MVC, и я заполнил базу данных с помощью Entity Framework 6 Code First. В студенческой организации есть два идентификатора города; один из них для BirthCity, другой для WorkCity. Когда я определяю внешние ключи, как указано выше, после миграции создается дополнительный столбец с именем City_ID в таблице Student. Id там ошибка или как определить эти FK? Заранее спасибо.Определение нескольких внешних ключей для одной и той же таблицы в Entity Framework Code First

Студент:

public class Student 
{ 
    public int ID { get; set; } 

    public string Name { get; set; } 

    public string Surname { get; set; } 

    public int BirthCityID { get; set; } 

    public int LivingCityID { get; set; } 


    [ForeignKey("BirthCityID")] 
    public virtual City BirthCity { get; set; } 

    [ForeignKey("LivingCityID")] 
    public virtual City LivingCity { get; set; } 
} 


Город:

public class City 
{ 
    public int ID { get; set; } 

    public string CityName { get; set; } 


    public virtual ICollection<Student> Students { get; set; } 
} 
+0

Я упростил свой код для ясности, но мой код точно похож на эту логику. С другой стороны, я уже пытался восстановить базу данных с нуля 2-3 раза, но теперь я делаю то же самое и сообщаю вам через несколько минут. –

+0

@ChrisPratt: Вот шаги, которые я выполнил сейчас: 1) Удаленная папка миграции и ее содержимое из моего проекта. 2) Очистите и перестройте весь проект в решении. 3) включить-миграции в консоли диспетчера пакетов, а затем добавить AutomaticMigrationsEnabled = true; и AutomaticMigrationDataLossAllowed = true; для конструктора конфигурационного файла. 4) Add-Migration. >>> –

+0

@ChrisPratt: Но после этого шага я снова заметил, что в методе Up есть свойство «City_ID = c.Int()». Тем не менее, я искал в решении, и нет свойства «City_ID». Итак, почему эта неопределенная поддержка была добавлена ​​в проект? Заранее спасибо. –

ответ

52

К достигните того, что вы хотите, чтобы обеспечить некоторую дополнительную конфигурацию. Код Первое соглашение может идентифицировать двунаправленные отношения, но не тогда, когда есть multi двунаправленные отношения между двумя объектами. Вы можете добавить конфигурацию (используя Аннотации данных или Fluent API), чтобы представить эту информацию строителю модели. С аннотациями данных вы будете использовать аннотацию под названием InverseProperty. С Fluent API вы будете использовать комбинацию методов Has/With, чтобы указать правильные концы этих отношений.

Использование данных аннотаций может выглядеть так:

public class Student 
{ 
    public int ID { get; set; } 

    public string Name { get; set; } 

    public string Surname { get; set; } 

    public int BirthCityID { get; set; } 

    public int LivingCityID { get; set; } 


    [ForeignKey("BirthCityID")] 
    [InverseProperty("Students")] 
    public virtual City BirthCity { get; set; } 

    [ForeignKey("LivingCityID")] 
    public virtual City LivingCity { get; set; } 
} 

Таким образом, вы явно указать, что вы хотите связать BirthCity свойства навигации с Students свойства навигации в другом конце связи.

Использование Fluent Апи может выглядеть так:

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
    modelBuilder.Entity<Student>().HasRequired(m => m.BirthCity) 
           .WithMany(m => m.Students).HasForeignKey(m=>m.BirthCityId); 
    modelBuilder.Entity<Student>().HasRequired(m => m.LivingCity) 
           .WithMany().HasForeignKey(m=>m.LivingCityId); 
} 

С помощью этого последнего решения вам не нужно использовать любой атрибут объявления.

Теперь предложение @ChristPratt в коллекции Student в вашем классе City для каждой взаимосвязи действительно полезно. Если вы сделаете это, то конфигурации с использованием данных аннотаций может быть таким образом:

public class Student 
{ 
    public int ID { get; set; } 

    public string Name { get; set; } 

    public string Surname { get; set; } 

    public int BirthCityID { get; set; } 

    public int LivingCityID { get; set; } 


    [ForeignKey("BirthCityID")] 
    [InverseProperty("BirthCityStudents")] 
    public virtual City BirthCity { get; set; } 

    [ForeignKey("LivingCityID")] 
    [InverseProperty("LivingCityStudents")] 
    public virtual City LivingCity { get; set; } 
} 

Или с помощью Fluent Апи следуя той же идее:

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
    modelBuilder.Entity<Student>().HasRequired(m => m.BirthCity) 
       .WithMany(m => m.BirthCityStudents).HasForeignKey(m=>m.BirthCityId); 
    modelBuilder.Entity<Student>().HasRequired(m => m.LivingCity) 
       .WithMany(m => m.LivingCityStudents).HasForeignKey(m=>m.LivingCityId); 
} 
+0

Большое спасибо за ваш ответ. Как вы и @ChrisPratt сказали, есть два способа достичь этого, и оба они работают. Поскольку ** Data Annotations ** кажется намного проще управлять объектами для этой операции, я бы предпочел использовать этот способ. Еще раз спасибо за ваше внимание. С уважением ... –

+0

Что делать, если отношения между Студентом и Сити были 1 -> 0..1? Как вы можете реализовать это с помощью свободного API –

+0

@PaulSodimu, взгляните на это [страница] (https://www.safaribooksonline.com/library/view/programming-entity-framework/9781449317867/ch04s07.html). Вы найдете ответ на свой вопрос – octavioccl

15

Sheesh. Это был долгий день. На самом деле на самом деле очень большая, вопиющая проблема с вашим кодом, что я полностью пропустил, когда прокомментировал.

Проблема в том, что вы используете одну коллекцию студентов на City. Что на самом деле происходит здесь, так это то, что EF не может решить, какой внешний ключ должен на самом деле отображать эту коллекцию, поэтому он создает другой внешний ключ, специально предназначенный для отслеживания этих отношений. Затем, по сути, у вас нет навигационных свойств для коллекций студентов, полученных от BirthCity и LivingCity.

Для этого вам нужно спуститься до полной конфигурации, так как нет возможности правильно настроить это, используя только аннотации данных. Вам также потребуется дополнительный сбор студентов, так что вы можете отслеживать как отношения:

public class City 
{ 
    ... 

    public virtual ICollection<Student> BirthCityStudents { get; set; } 
    public virtual ICollection<Student> LivingCityStudents { get; set; } 
} 

Тогда для Student:

public class Student 
{ 
    ... 

    public class StudentMapping : EntityTypeConfiguration<Student> 
    { 
     public StudentMapping() 
     { 
      HasRequired(m => m.BirthCity).WithMany(m => m.BirthCityStudents); 
      HasRequired(m => m.LivingCity).WithMany(m => m.LivingCityStudents); 
     } 
    } 
} 

И, наконец, в вашем контексте:

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
    modelBuilder.Configurations.Add(new Student.StudentMapping()); 
} 
+0

Большое спасибо за ваш ответ. Как вы и @octavioccl сказали, есть два способа достичь этого, и оба они работают. Поскольку * Data Annotations * кажется, намного проще управлять объектами для этой операции, я бы предпочел использовать этот способ. Еще раз спасибо за ваше внимание. С уважением ... –

+0

Спасибо за класс 'StudentMapping' ... Я боролся с тем, где эти определения должны жить, и мне очень нравится, что они видны из класса« Студент », где информация важна, а не в контекст. – erroric

+1

@error: Да, я не могу вспомнить, где я наткнулся на это изначально, но он намного лучше, чем придерживаться его в контексте, поскольку каждый учебник повсюду показывает вам. Прежде чем открывать этот подход, моя самая большая проблема с плавным конфигом заключалась в том, что это не было с сущностью, поэтому, если вы не помните о том, какие объекты были настроены через контекст, это приводит к большой путанице. –

Смежные вопросы