实体框架:迁移错误地将外键列添加两次

时间:2016-09-05 14:45:02

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

我在CodeFirst中使用 EntityFramework 6.1.3 作为Asp.Net应用程序。我有一个现有的表("用户"),我试图添加一个外键" GroupId" 。 这是类(Everything with // 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识别出外键但仍然添加了默认的&#34; Group_Id&#34;。如果我使用它,并更新数据库,导航属性&#34; Group&#34;将转移到&#34; Group_Id&#34;而不是所期望的&#34; GroupId&#34;。

任何可能导致这种情况的想法?

更新1

我评论了导航属性并获得了相同的结果。看起来关系另一端的ICollection 用户是罪魁祸首。这是&#34; Group&#34;

[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 用户,则在添加迁移时会出现以下异常:

  

无法确定类型&#39; SparePartsDb.Entities.Group&#39;之间关联的主要结尾。和&#39; SparePartsDb.Entities.User&#39;。必须使用关系流畅API或数据注释显式配置此关联的主要结尾。

更新2

经过一些谷歌搜索并更改了Group类的Key Attribute ...

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

...我能够注释掉ICollection并运行迁移。但是,即使导航属性出现在用户类中,GroupId也不会再被识别为外键。

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

2 个答案:

答案 0 :(得分:1)

好吧,所以我在多个排列中搜索了“添加重复的外键的迁移”,但是我终于弄清楚了是什么原因造成的:不要以为Fluent API知道您指的是哪个字段用WithOne()WithMany()来指定字段。有关详情,请参见下文。

我有一对多关系的两个实体模型AddressInvitationpublic virtual ICollection<Invitation> Invitations上的Address和{{ 1}}。我选择使用Fluent API而不是使用约定/属性,因此我的public virtual Address Address构建器如下所示:

Invitation

不幸的是,,EF Core 2.2不喜欢那里有空的Invitation。运行builder .HasOne(x => x.Address) .WithMany() .HasForeignKey(x => x.AddressId); 会导致这种废话,就像OP:

WithMany()

将我的构建器切换为:

dotnet ef migrations add InitialCreate

为我解决了这个问题。

先运行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

希望这可以帮助其他一些可怜的灵魂对此进行搜索。

答案 1 :(得分:0)

您不需要输入

[Column("GroupId")] 

之上
public int? GroupId { get; set; } 

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

如msdn所述:

  

生成数据库时,代码首先在Post类中看到BlogId属性,并通过约定类名加“Id”的约定将其识别为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