【问题标题】:Entity Framework: Migration falsely adds foreign key column twice实体框架:迁移错误地添加了两次外键列
【发布时间】:2017-01-12 23:15:17
【问题描述】:

我将 EntityFramework 6.1.3 与 CodeFirst 一起用于 Asp.Net 应用程序。我有一个现有表(“用户”),我正在尝试在其上添加外键 “GroupId”。 这是类(带有 //new 的所有内容都是上次迁移后所做的更改)

[Table("Users")]
public class User
{
    [Key]
    [Column("PK_USER")]
    public int Id { get; set; }
    [Column("Username")]
    public string Username { get; set; }
    [Column("Name")]
    public string Name { get; set; }
    [Column("Password")]
    public string Password { get; set; }
    [Column("Firstname")]
    public string Firstname { get; set; }
    [Column("Lastname")]
    public string Lastname { get; set; }
    [Column("LastLogin")]
    public DateTime? LastLogin { get; set; }
    [Column("Department")]
    public string Department { get; set; }
    [Column("EMail")]
    public string EMail { get; set; }
    [Column("IsWindowsUser")]
    public bool? IsWindowsUser { get; set; }
    [Column("Signature")]
    public string Signature { get; set; }

    [Column("FK_ROLE")]
    public int? RoleId { get; set; }
    [ForeignKey("RoleId")]
    public virtual Role Role { get; set; }

    // new
    [Column("GroupId")]
    public int? GroupId { get; set; }

    //new
    [ForeignKey("GroupId")]
    public virtual Group Group { get; set; }

    //new
    public virtual ICollection<GroupResponsibility> Responsibilites { get; set; }

    public virtual ICollection<UserField> UserFields { get; set; }

    public override string ToString()
    {
        return Username;
    }

}

在我运行 add-migration 后会生成以下代码(省略其他更改)

        AddColumn("dbo.Users", "GroupId", c => c.Int());
        AddColumn("dbo.Users", "Group_Id", c => c.Int());
        CreateIndex("dbo.Users", "GroupId");
        CreateIndex("dbo.Users", "Group_Id");
        AddForeignKey("dbo.Users", "Group_Id", "dbo.Groups", "Id");
        AddForeignKey("dbo.Users", "GroupId", "dbo.Groups", "Id");

如您所见,EntityFramework 识别了外键,但仍添加了一个默认的“Group_Id”。如果我通过它并更新数据库,导航属性“Group”将关联到“Group_Id”而不是所需的“GroupId”。

有什么可能导致这种情况的想法吗?

更新 1

我注释掉了导航属性并得到了相同的结果。看起来关系另一端的 ICollection Users 是罪魁祸首。这是类“组”

[Table("Groups")]
public class Group
{
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
    public int? GroupLeaderId { get; set; }

    [ForeignKey("GroupLeaderId")]
    public virtual User GroupLeader { get; set; }

    public virtual ICollection<User> Users { get; set; }
    public virtual ICollection<GroupResponsibility> Responsibilites { get; set; }
    public virtual ICollection<ApplicationForm> Applications { get; set; }

    public override string ToString()
    {
        return Name;
    }
}

如果我注释掉 ICollection Users,我会在添加迁移时收到以下异常:

无法确定类型“SparePartsDb.Entities.Group”和“SparePartsDb.Entities.User”之间关联的主体端。此关联的主体端必须使用关系流式 API 或数据注释显式配置。

更新 2

经过一番谷歌搜索并更改 Group 类的 Key 属性...

    [Key, ForeignKey("GroupLeader")]
    public int Id { get; set; }

...我可以注释掉 ICollection 并运行迁移。但是,GroupId 不再被识别为外键,即使导航属性存在于 User 类中。

AddColumn("dbo.Users", "GroupId", c => c.Int());

【问题讨论】:

  • 看起来是正确的。多余的列定义不应该影响它(如果它们与属性名称匹配,则不需要列注释)。我会尝试注释掉 nav 属性,看看生成了什么。
  • @SteveGreene:我注释掉了导航属性并得到了相同的结果。看起来关系另一端的 ICollection 用户是罪魁祸首。我更新了我的帖子。

标签: c# sql-server entity-framework ef-code-first


【解决方案1】:

好的,所以我已经在多个排列中搜索了几个小时的“迁移添加重复外键”,但我终于弄清楚了是什么原因造成的:不要假设 Fluent API 知道您指的是哪个字段使用WithOne()WithMany()-- 指定字段。详情见下文。

我有两个处于一对多关系中的实体模型,AddressInvitationpublic virtual ICollection&lt;Invitation&gt; InvitationsAddress 上,public virtual Address AddressInvitation 上。我选择 Fluent API 而不是约定/属性,所以我的 Invitation 构建器看起来像这样:

builder
  .HasOne(x => x.Address)
  .WithMany()
  .HasForeignKey(x => x.AddressId);

不幸的是,EF Core 2.2 不喜欢在里面放那个空的WithMany()。运行 dotnet ef migrations add InitialCreate 导致这种废话,很像 OP:

migrationBuilder.CreateTable(
  name: "Invitation",
  columns: table => new
  {
    AddressId = table.Column<Guid>(nullable: false),
    AddressId1 = table.Column<Guid>(nullable: true),
    ...
  },
  constraints: table =>
  {
    table.PrimaryKey("PK_Invitation", x => x.Id);
    table.ForeignKey(
      name: "FK_Invitation_Address_AddressId",
      column: x => x.AddressId,
      principalTable: "Address",
      principalColumn: "Id",
      onDelete: ReferentialAction.Cascade);
    table.ForeignKey(
      name: "FK_Invitation_Address_AddressId1",
      column: x => x.AddressId1,
      principalTable: "Address",
      principalColumn: "Id",
      onDelete: ReferentialAction.Restrict);
  });

将我的构建器切换为阅读:

builder
  .HasOne(x => x.Address)
  .WithMany(x => x.Invitations)
  .HasForeignKey(x => x.AddressId);

帮我解决了这个问题。

运行 dotnet ef migrations remove 后跟 dotnet ef migrations add InitialCreate 再次给了我一个更好的迁移:

migrationBuilder.CreateTable(
  name: "Invitation",
  columns: table => new
  {
    AddressId = table.Column<Guid>(nullable: false),
    ...
  },
  constraints: table =>
  {
    table.PrimaryKey("PK_Invitation", x => x.Id);
    table.ForeignKey(
      name: "FK_Invitation_Address_AddressId",
      column: x => x.AddressId,
      principalTable: "Address",
      principalColumn: "Id",
      onDelete: ReferentialAction.Cascade);
  });

希望这可以帮助其他一些可怜的灵魂寻找这个。

【讨论】:

  • 是的 ffs ms 请修复此问题,谢谢!我在使用 EF 3.1 (2020-03-27) 时遇到了这个问题
  • 这对 EF 6.4 并没有修复,但对 Core 有修复
【解决方案2】:

你不应该放一个

[Column("GroupId")] 

在上面

public int? GroupId { get; set; } 

实体框架应该能够自行识别您的映射。

如msdn中所述:

在生成数据库时,代码首先会看到 Post 类中的 BlogId 属性,并按照与类名加“Id”相匹配的约定将其识别为 Blog 类的外键。但是 blog 类中没有 BlogId 属性。解决方案是在 Post 中创建一个导航属性,并使用 Foreign DataAnnotation 帮助代码首先了解如何建立两个类之间的关系——使用 Post.BlogId 属性——以及如何在数据库中指定约束.

这个解释的代码是:

public class Post 
{ 
    public int Id { get; set; } 
    public string Title { get; set; } 
    public DateTime DateCreated { get; set; } 
    public string Content { get; set; } 
    public int BlogId { get; set; } 
    [ForeignKey("BlogId")] 
    public Blog Blog { get; set; } 
    public ICollection<Comment> Comments { get; set; } 
}

如您所见,只有复杂对象具有定义 fk 的映射,EF 应该为您完成其余的工作。

source

【讨论】:

  • 感谢您的详细解释。但是,多余的列属性似乎没有什么不同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-07-01
  • 2013-09-17
  • 1970-01-01
  • 2018-11-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多