首页 > 解决方案 > 无法定义没有 PK 的表

问题描述

我有一个名为的表AspNetUsers,它是由创建的默认表AspNetCore.Identity,我创建了一个名为的类User,它继承IdentityUser并实现了更多字段AspNetUsers

public class User : IdentityUser
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
    public string LockoutMessage { get; set; }
    public string SessionId { get; set; }

    public virtual UserDetails UserDetail { get; set; }
}

然后我创建了另一个名为 的类UserDetails,我不需要PK在这个表中,因为UserDetails我需要存储可用用户的详细信息,User其中是AspNetUsers表:

public class UserDetails
{
    //public string Id { get; set; }
    public string Biography { get; set; }
    public string Country { get; set; }
    public string FacebookLink { get; set; }
    public string TwitterLink { get; set; }
    public string SkypeLink { get; set; }

    public string UserId { get; set; }
    public virtual User User { get; set; }
}

现在,如果我取消注释该Id属性,则迁移效果很好,但我不需要,Id所以我注释了该属性并执行此命令:

add-migration Initial-Migration -context DemoAppContext
update-database

DemoAppContext我告诉要EF实现FKon的类中UserDetails

public class DemoAppContext : IdentityDbContext<User>
{
    public DemoAppContext(DbContextOptions<DemoAppContext> options) : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        builder.Entity<User>(entity =>
        {
            entity.HasOne(d => d.UserDetail)
                  .WithOne(p => p.User)
                  .HasForeignKey<UserDetails>(x => x.UserId);
        });
    }

    public DbSet<UserDetails> UserDetails { get; set; }
}

命令后Add-Migration我收到此错误:

实体类型“UserDetails”需要定义一个主键。

正如我所说,如果我注释掉Id所有工作正常,但我不需要PKon UserDetails

标签: c#asp.netentity-frameworkasp.net-core

解决方案


正如我所说,如果我注释掉 Id 一切正常,但我不需要对 UserDetails 进行 PK

如果我理解正确,您只是不想添加额外的列(例如,IdUserDetailsId)设置为 PK 。UserDetails您要创建的表将有一个引用UserId该表的User表,并且UserId将同时用作主键和外键。

事实上,没有必要拥有entity的Id属性。UserDetails那么为什么你的代码不起作用呢?原因是您混淆了从属实体和主体实体。

HasForeignKey<>()方法的原型是:

public virtual ReferenceReferenceBuilder<TEntity,TRelatedEntity> HasForeignKey<TDependentEntity> 
    (
        Expression<Func<TDependentEntity,object>> foreignKeyExpression
    ) 
    where TDependentEntity : class;

HasForeignKey<TDependentEntity>()注意代表依赖实体的通用参数。

这是你的代码:

builder.Entity<User>(entity =>
{
    entity.HasOne(d => d.UserDetail)
            .WithOne(p => p.User)
        .HasForeignKey<UserDetails>(x => x.UserId);
});

看到了吗?在您的代码中,entity这里是UserPrincipal Entity 而不是 Dependent Entity。要修复它,请更改您的代码,如下所示:

protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);

    builder.Entity<User>(entity =>
    {
        entity.HasOne(u => u.UserDetail).WithOne(d => d.User);
    });

    builder.Entity<UserDetails>(entity =>
    {
        // set the UserId as key
        entity.HasKey(d=>d.UserId);

        // the relationship between `UserDetails : User`  is  1-to-1
        entity.HasOne(d=>d.User).WithOne(u=>u.UserDetail)
            // set column `UserId` as the FK for the dependent entity , i.e. , the `UserDetails` .
            .HasForeignKey<UserDetails>(u=>u.UserId); 
    });
}

它将同时生成一个列设置为 PrimaryKey 和 ForeignKey 的UserDetails表:UserId

CREATE TABLE [dbo].[UserDetails] (
    [Biography]    NVARCHAR (MAX) NULL,
    [Country]      NVARCHAR (MAX) NULL,
    [FacebookLink] NVARCHAR (MAX) NULL,
    [TwitterLink]  NVARCHAR (MAX) NULL,
    [SkypeLink]    NVARCHAR (MAX) NULL,
    [UserId]       NVARCHAR (450) NOT NULL,
    CONSTRAINT [PK_UserDetails] PRIMARY KEY CLUSTERED ([UserId] ASC),
    CONSTRAINT [FK_UserDetails_AspNetUsers_UserId] FOREIGN KEY ([UserId]) REFERENCES [dbo].[AspNetUsers] ([Id]) ON DELETE CASCADE
);

顺便说一句,Visual Studio 的 SQL Server 对象资源管理器中似乎存在一个错误(请参阅我的问题here),导致我们无法看到已经存在的 FK 约束的错误。

但是,我们可以通过尝试使用不存在的 UserId 插入一行来证明这一点:

insert into UserDetails 
    (Biography,UserId) 
values 
    ('hello,wrold','here-is-a-non-existing-user-id')

它将按预期抱怨以下消息:

消息 547,第 16 级,状态 0,第 1 行

INSERT 语句与 FOREIGN KEY 约束“FK_UserDetail_AspNetUsers_UserId”冲突。冲突发生在数据库“App-EFCore-FK-Test”、表“dbo.AspNetUsers”、“Id”列中。

该语句已终止。


推荐阅读