首先在实体框架代码中为同一个表定义多个外键

时间:2022-10-05 15:23:37

I have two entities in my MVC application and I populated the database with Entity Framework 6 Code First approach. There are two city id in the Student entity; one of them for BirthCity, the other for WorkingCity. When I define the foreign keys as above an extra column is created named City_ID in the Student table after migration. Id there a mistake or how to define these FKs? Thanks in advance.

我的MVC应用程序中有两个实体,我使用Entity Framework 6代码优先方法填充数据库。学生实体中有两个城市id;其中一个是出生地,另一个是工作城市。当我在上面定义外键时,在迁移后的Student表中创建了一个名为City_ID的额外列。我是不是弄错了,或者如何定义这些FKs?提前谢谢。

Student:

学生:

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; }
}


City:

城市:

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

    public string CityName { get; set; }


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

2 个解决方案

#1


53  

To achieve what you want you need to provide some aditional configuration.Code First convention can identify bidirectional relationships, but not when there are multiple bidirectional relationships between two entities.You can add configuration (using Data Annotations or the Fluent API) to present this information to the model builder. With Data Annotations, you’ll use an annotation called InverseProperty. With the Fluent API, you’ll use a combination of the Has/With methods to specify the correct ends of these relationships.

要实现您想要的,您需要提供一些传统的配置。代码优先约定可以识别双向关系,但不能识别两个实体之间存在多个双向关系的情况。您可以添加配置(使用数据注释或Fluent API)来向模型构建器显示这些信息。对于数据注释,您将使用名为InverseProperty的注释。使用Fluent API,您将使用have /With方法的组合来指定这些关系的正确端点。

Using Data Annotations could be like this:

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

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; }
}

This way you specifying explicitly that you want to relate the BirthCity navigation property with Students navigation property in the other end of the relationship.

通过这种方式,您可以明确地指定您希望在关系的另一端将诞生城市导航属性与学生导航属性关联起来。

Using Fluent Api could be like this:

使用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);
}

With this last solution you don't need to use any attibute.

对于最后一种解决方案,您不需要使用任何attibute。

Now, the suggestion of @ChristPratt in have a collection of Student in your City class for each relationship is really useful. If you do that, then the configurations using Data Annotations could be this way:

现在,@ChristPratt的建议在你的城市班级里有一个学生的集合是非常有用的。如果这样做,那么使用数据注释的配置可以是这样的:

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; }
}

Or using Fluent Api following the same idea:

或者使用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);
}

#2


16  

Sheesh. It's been a long day. There's actually a very big, glaring problem with your code, actually, that I completely missed when I commented.

天哪。这是漫长的一天。实际上,您的代码有一个非常大的、明显的问题,我在注释时完全没有注意到这个问题。

The problem is that you're using a single collection of students on City. What's actually happening here is that EF can't decide which foreign key it should actually map that collection to, so it creates another foreign key specifically to track that relationship. Then, in effect you have no navigation properties for the collections of students derived from BirthCity and LivingCity.

问题是你使用的是一个城市的学生。这里实际发生的情况是EF不能决定它应该将集合映射到哪个外键,所以它创建了另一个外键来跟踪这个关系。然后,实际上,你没有任何导航属性的学生集合从诞生城市和生活城市。

For this, you have to drop down to fluent configuration, as there's no way to configure this properly using just data annotations. You'll also need an additional collection of students so you can track both relationships:

为此,您必须降低到fluent配置,因为仅使用数据注释无法正确配置它。你还需要一个额外的学生集合,以便你可以跟踪这两种关系:

public class City
{
    ...

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

Then, for 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);
        }
    }
}

And finally in your context:

最后在你的背景下

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

#1


53  

To achieve what you want you need to provide some aditional configuration.Code First convention can identify bidirectional relationships, but not when there are multiple bidirectional relationships between two entities.You can add configuration (using Data Annotations or the Fluent API) to present this information to the model builder. With Data Annotations, you’ll use an annotation called InverseProperty. With the Fluent API, you’ll use a combination of the Has/With methods to specify the correct ends of these relationships.

要实现您想要的,您需要提供一些传统的配置。代码优先约定可以识别双向关系,但不能识别两个实体之间存在多个双向关系的情况。您可以添加配置(使用数据注释或Fluent API)来向模型构建器显示这些信息。对于数据注释,您将使用名为InverseProperty的注释。使用Fluent API,您将使用have /With方法的组合来指定这些关系的正确端点。

Using Data Annotations could be like this:

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

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; }
}

This way you specifying explicitly that you want to relate the BirthCity navigation property with Students navigation property in the other end of the relationship.

通过这种方式,您可以明确地指定您希望在关系的另一端将诞生城市导航属性与学生导航属性关联起来。

Using Fluent Api could be like this:

使用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);
}

With this last solution you don't need to use any attibute.

对于最后一种解决方案,您不需要使用任何attibute。

Now, the suggestion of @ChristPratt in have a collection of Student in your City class for each relationship is really useful. If you do that, then the configurations using Data Annotations could be this way:

现在,@ChristPratt的建议在你的城市班级里有一个学生的集合是非常有用的。如果这样做,那么使用数据注释的配置可以是这样的:

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; }
}

Or using Fluent Api following the same idea:

或者使用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);
}

#2


16  

Sheesh. It's been a long day. There's actually a very big, glaring problem with your code, actually, that I completely missed when I commented.

天哪。这是漫长的一天。实际上,您的代码有一个非常大的、明显的问题,我在注释时完全没有注意到这个问题。

The problem is that you're using a single collection of students on City. What's actually happening here is that EF can't decide which foreign key it should actually map that collection to, so it creates another foreign key specifically to track that relationship. Then, in effect you have no navigation properties for the collections of students derived from BirthCity and LivingCity.

问题是你使用的是一个城市的学生。这里实际发生的情况是EF不能决定它应该将集合映射到哪个外键,所以它创建了另一个外键来跟踪这个关系。然后,实际上,你没有任何导航属性的学生集合从诞生城市和生活城市。

For this, you have to drop down to fluent configuration, as there's no way to configure this properly using just data annotations. You'll also need an additional collection of students so you can track both relationships:

为此,您必须降低到fluent配置,因为仅使用数据注释无法正确配置它。你还需要一个额外的学生集合,以便你可以跟踪这两种关系:

public class City
{
    ...

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

Then, for 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);
        }
    }
}

And finally in your context:

最后在你的背景下

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