在Entity Framework Code First中为同一个表定义多个外键

Jac*_*ack 36 asp.net-mvc entity-framework foreign-keys foreign-key-relationship ef-code-first

我的MVC应用程序中有两个实体,我使用Entity Framework 6 Code First方法填充数据库.学生实体中有两个城市ID; 其中一个为BirthCity,另一个为WorkingCity.当我在上面定义外键时,在迁移后在Student表中创建一个名为City_ID的额外列.我有错误或如何定义这些FK?提前致谢.

学生:

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; }
}
Run Code Online (Sandbox Code Playgroud)


市:

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

    public string CityName { get; set; }


    public virtual ICollection<Student> Students { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

oct*_*ccl 58

要实现您想要的功能,您需要提供一些aditional configuration.Code第一个约定可以识别双向关系,但不能在两个实体之间存在多个双向关系时.您可以添加配置(使用Data AnnotationsFluent API)来显示此信息到模型构建者.使用Data Annotations,您将使用一个名为的注释InverseProperty.使用Fluent API,您将使用Has/ Withmethods 的组合来指定这些关系的正确结束.

使用数据注释可以是这样的:

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; }
}
Run Code Online (Sandbox Code Playgroud)

这样,您明确指定要将BirthCity导航属性与Students关系另一端的导航属性相关联.

使用Fluent Api可能是这样的:

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);
}
Run Code Online (Sandbox Code Playgroud)

使用最后一个解决方案,您无需使用任何属性.

现在,@ChristPratt的建议Student在你的City班级中为每个关系都有一个集合是非常有用的.如果您这样做,那么使用Data Annotations的配置可能是这样的:

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; }
}
Run Code Online (Sandbox Code Playgroud)

或者使用Fluent Api遵循相同的想法:

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);
}
Run Code Online (Sandbox Code Playgroud)


Chr*_*att 18

啧.这是漫长的一天.实际上,你的代码实际上存在一个非常大的,明显的问题,当我评论时,我完全错过了.

问题是你正在使用一个学生集合City.这里实际发生的是EF无法决定它应该将该集合实际映射到哪个外键,因此它会创建另一个外键来专门跟踪该关系.然后,实际上你没有导出来自BirthCity和的学生集合的导航属性LivingCity.

为此,您必须下拉到流畅的配置,因为没有办法仅使用数据注释来正确配置.您还需要一个额外的学生集合,以便您可以跟踪这两种关系:

public class City
{
    ...

    public virtual ICollection<Student> BirthCityStudents { get; set; }
    public virtual ICollection<Student> LivingCityStudents { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后,为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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

最后在你的背景下:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Configurations.Add(new Student.StudentMapping());
}
Run Code Online (Sandbox Code Playgroud)

  • @erroric:是的,我不记得我最初在哪里偶然发现了这个,但它比将它粘贴在上下文中要好得多,因为到处都向你展示了每一个教程。在发现这种方法之前,我对 fluent config 的一个最大问题是它与实体无关,因此除非您注意哪些实体是通过上下文配置的,否则会导致很多混乱。 (2认同)