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

Asp.net mvc 首先在实体框架代码中为同一个表定义多个外键,asp.net-mvc,entity-framework,ef-code-first,foreign-keys,foreign-key-relationship,Asp.net Mvc,Entity Framework,Ef Code First,Foreign Keys,Foreign Key Relationship,我的MVC应用程序中有两个实体,我使用实体框架6代码优先的方法填充数据库。学生实体中有两个城市id;一个代表出生地,另一个代表工作地。当我如上所述定义外键时,迁移后会在学生表中创建一个名为City_ID的额外列。是否存在错误或如何定义这些FK?提前谢谢 学生: public class Student { public int ID { get; set; } public string Name { get; set; } public string Surname

我的MVC应用程序中有两个实体,我使用实体框架6代码优先的方法填充数据库。学生实体中有两个城市id;一个代表出生地,另一个代表工作地。当我如上所述定义外键时,迁移后会在学生表中创建一个名为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; }
}
public class City
{
    public int ID { get; set; }

    public string CityName { get; set; }


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

城市:

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; }
}
公共级城市
{
公共int ID{get;set;}
公共字符串CityName{get;set;}
公共虚拟ICollection学生{get;set;}
}

嘘。这是漫长的一天。实际上,你的代码有一个非常明显的问题,我在评论时完全没有注意到

问题是,您使用的是
城市
上的单个学生集合。这里实际发生的是EF无法决定它应该将集合映射到哪个外键,所以它会创建另一个外键来专门跟踪该关系。然后,实际上,对于从
BirthCity
LivingCity
派生的学生集合,您没有导航属性

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

public class City
{
    ...

    public virtual ICollection<Student> BirthCityStudents { get; set; }
    public virtual ICollection<Student> LivingCityStudents { get; set; }
}
最后,在你的背景下:

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

要实现您想要的,您需要提供一些传统的配置。代码优先约定可以识别双向关系,但不能在存在双向关系时识别 两个实体之间的多个双向关系。您可以添加配置(使用数据注释或Fluent API)来表示此关系 将信息发送给模型生成器。对于数据注释,您将使用注释 打电话。在Fluent API中,您将使用
Has
/
方法的组合来指定这些关系的正确端点

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

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; }
}
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);
}
通过这种方式,您可以明确指定希望将
出生地
导航属性与关系另一端的
学生
导航属性相关联

使用Fluent Api可能是这样的:

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; }
}
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);
}
或者使用流畅的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);
}
模型创建时受保护的覆盖无效(DbModelBuilder modelBuilder)
{
modelBuilder.Entity().HasRequired(m=>m.BirthCity)
.有许多(m=>m.BirthCityStudents)。有foreignkey(m=>m.BirthCityId);
modelBuilder.Entity().HasRequired(m=>m.LivingCity)
.有很多(m=>m.LivingCity学生)。有外键(m=>m.LivingCity ID);
}

为了清晰起见,我简化了代码,但代码的逻辑与此完全相似。另一方面,我已经尝试从头开始重新创建数据库2-3次,但现在我正在重新创建数据库,并在几分钟后通知您。@ChrisPratt:以下是我现在执行的步骤:1)从我的项目中删除迁移文件夹及其内容。2) 清理并重建解决方案中的所有项目。3) 在Package Manager控制台上启用迁移,然后添加AutomaticMiggerationsEnabled=true;和AutomaticMigrationDataLossAllowed=true;到配置文件的构造函数。4) 添加迁移。>>>@ChrisPratt:但在这一步之后,我再次观察到Up方法中有一个属性“City_ID=c.Int(),”。但是,我在解决方案中进行了搜索,没有“City_ID”属性。那么,为什么这个未定义的属性被添加到项目中呢?提前谢谢。非常感谢你的回答。正如你和@ChrisPratt所说,有两种方法可以实现这一点,而且两种方法都在发挥作用。由于数据注释方式似乎更容易控制此操作的实体,所以我更愿意使用这种方式。再次感谢您的考虑。关于…如果学生和城市之间的关系是1->0..1怎么办?你如何用fluent实现这一点API@octavioccl这个答案正是应该如何处理的。这将在DB中创建正确的导航以及正确的外键集。我发现InverseProperty和ForeignKey属性是最容易读取代码并实际说出它在做什么的属性。我必须使
BirthCityID
HomeCityID
为空(
int?
)才能使数据注释方法工作。否则我总是会遇到错误SqlException:在表“…”上引入外键约束“…”可能会导致循环或多个级联路径。City类中的Students属性在做什么?非常感谢您的回答。正如您和@octavioccl所说,有两种方法可以实现这一点,而且它们都在发挥作用。由于数据注释方式似乎更容易控制此操作的实体,所以我更愿意使用这种方式。再次感谢您的考虑。问候…感谢
StudentMapping
课程。。。我一直在琢磨这些定义应该放在哪里,我真的很喜欢它们可以从
学生
课堂上看到-其中的信息是相关的-而不是在上下文中。@erroric:是的,我不记得我最初是在哪里发现的,但这比把它放在上下文中要好得多,因为每一个教程都向你展示了这一点。在发现这种方法之前,我对fluent config的最大问题是它与实体无关,因此除非您注意到哪些实体是通过上下文配置的,否则它会导致很多混乱。谢谢,帮我走出困境!