为什么EF生成外键

本文关键字:EF 为什么 | 更新日期: 2023-09-27 18:27:33

我需要一对一(可选)。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<PinnacleAccount>().HasKey(x => x.Id);
    modelBuilder.Entity<PinnacleAccount>()
        .HasRequired(x => x.User)
        .WithOptional(x => x.PinnacleAccount);
    base.OnModelCreating(modelBuilder);
}

当我运行"添加迁移初始化"时,我检查生成的迁移,并查看:

CreateTable(
                "dbo.PinnacleAccounts",
                c => new
                    {
                        Id = c.Int(nullable: false, identity: true),
                        ClientId = c.String(),
                        Password = c.String(),
                        PercentForBet = c.Int(nullable: false),
                        UserId = c.String(),
                        User_Id = c.String(nullable: false, maxLength: 128),
                    })
                .PrimaryKey(t => t.Id)
                .ForeignKey("dbo.AspNetUsers", t => t.User_Id)
                .Index(t => t.User_Id);

但我有属性UserId。为什么ef创建User_Id

   public class ApplicationUser : IdentityUser
    {
        public virtual PinnacleAccount PinnacleAccount { get; set; }
        public int? PinnacleAccountId { get; set; }
        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
            // Add custom user claims here
            return userIdentity;
        }
    }

为什么EF生成外键

在实体框架中建立1:0.1关系时,第一个实体的主键必须与第二个实体的私钥相同。不能指定哪个特性是FK,因为它不是必需的。我将解释:

如果User只有一个PinnacleAccount,则为1:0.1关系。因此,每个PinnacleAccount都属于一个User。这意味着,PinnacleAccount是一个弱实体,因此,它的主键也是一个User外键。

PinnacleAccount不应该有自己的Id,而应该只有UserId。所以PinnacleAccount应该是这样的:

public class PinnacleAccount
{
    public string UserId { get; set; } //PK AND FK
    public string ClientId  { get; set; }
    public string Password { get; set; }
    public string PercentForBet { get; set; }
}

映射:

modelBuilder.Entity<PinnacleAccount>().HasKey(x => x.UserId);
modelBuilder.Entity<User>()
    .HasOptional(i => i.PinnacleAccount)
    .WithRequired(x => x.User);

这是建立1:0.1关系的唯一方法。

希望它能有所帮助!

也许您需要在映射关系的模型中声明

this.HasOptional(t => t.User)
                .WithMany(t => t.PinnacleAccount)
                .HasForeignKey(d => d.UserId);

正如您所知,EF基于约定、属性和您正在使用的Fluent API创建模型和相应的迁移。而且,除非你另有说明,否则约定只是在大多数情况下都能正常工作的内置约定。

由于您没有明确指定FK特性,因此正在使用约定。"通常"的约定会选择UserId作为FK。但是,类型不匹配,因为PinnacleAccountUserId只是一个字符串,而根据创建的User_IdAspNetUsers表上的主键),UserId是一个长度为128的字符串。

因此,如果使用[MaxLenght]属性或.hasMaxLength() fluent API将UserId的长度定义为128,则约定会将其用于外键。

@Fabio Luz给出了正确的答案,但对于任何已经建立了错误关系并需要纠正的人,以下是我的做法。

我的"PinnacleAccount"看起来如下(我使用GUID,但你明白了):

public class PinnacleAccount
{
  [Required]
  [Key]
  [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
  public Guid Id { get; set; }
  public virtual ApplicationUser User { get; set;  }
  [Required]
  public virtual string UserId { get; set; }  
}

我可以从PinnacleAccount访问用户,但不能反过来,我想纠正这一点。为此,我必须创建两个迁移。

首先,我将UserId属性编辑为UserIdOld,并创建了如下迁移:

public override void Up()
{
    DropForeignKey("dbo.PinnacleAccount", "UserId", "dbo.AspNetUsers");
    DropIndex("dbo.PinnacleAccount", new[] { "UserId" });
    RenameColumn(table: "dbo.PinnacleAccount", name: "UserId", newName: "User_Id");
    AddColumn("dbo.PinnacleAccount", "UserIdOld", c => c.String(nullable: false));
    AlterColumn("dbo.PinnacleAccount", "User_Id", c => c.String(maxLength: 128));
    CreateIndex("dbo.PinnacleAccount", "User_Id");
    AddForeignKey("dbo.PinnacleAccount", "User_Id", "dbo.AspNetUsers", "Id");
}
public override void Down()
{
    DropForeignKey("dbo.PinnacleAccount", "User_Id", "dbo.AspNetUsers");
    DropIndex("dbo.PinnacleAccount", new[] { "User_Id" });
    AlterColumn("dbo.PinnacleAccount", "User_Id", c => c.String(nullable: false, maxLength: 128));
    DropColumn("dbo.PinnacleAccount", "UserIdOld");
    RenameColumn(table: "dbo.PinnacleAccount", name: "User_Id", newName: "UserId");
    CreateIndex("dbo.PinnacleAccount", "UserId");
    AddForeignKey("dbo.PinnacleAccount", "UserId", "dbo.AspNetUsers", "Id", cascadeDelete: true);
}

这允许我释放UserId列名称,并用UserId填充User_Id列(因为这是alter语句)。

然后我把类改成这样:

 public class PinnacleAccount
{
    public string UserId { get; set; }
  public virtual ApplicationUser User { get; set;  }
}

随后,@Fabio Luz对映射进行了更改。

然后我创建了第二个迁移,看起来像:

public override void Up()
{
    DropIndex("dbo.PinnacleAccount", new[] { "User_Id" });
    RenameColumn(table: "dbo.PinnacleAccount", name: "User_Id", newName: "UserId");
    DropPrimaryKey("dbo.PinnacleAccount");
    AlterColumn("dbo.PinnacleAccount", "UserId", c => c.String(nullable: false, maxLength: 128));
    AddPrimaryKey("dbo.PinnacleAccount", "UserId");
    CreateIndex("dbo.PinnacleAccount", "UserId");
    DropColumn("dbo.PinnacleAccount", "Id");
    DropColumn("dbo.PinnacleAccount", "UserIdOld");
}
public override void Down()
{
    AddColumn("dbo.PinnacleAccount", "UserIdOld", c => c.String(nullable: false));
    AddColumn("dbo.PinnacleAccount", "Id", c => c.Guid(nullable: false, identity: true));
    DropIndex("dbo.PinnacleAccount", new[] { "UserId" });
    DropPrimaryKey("dbo.PinnacleAccount");
    AlterColumn("dbo.PinnacleAccount", "UserId", c => c.String(maxLength: 128));
    AddPrimaryKey("dbo.PinnacleAccount", "Id");
    RenameColumn(table: "dbo.PinnacleAccount", name: "UserId", newName: "User_Id");
    CreateIndex("dbo.PinnacleAccount", "User_Id");
}

这给了我正确的1:0.1。感谢法比奥展示了如何正确创建一个!