没有PK就无法定义表

问题描述 投票:2回答:1

我有一个名为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课程中,我告诉EFFK上实施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一切运作良好,但我不需要在PK上的UserDetails

c# asp.net entity-framework asp.net-core
1个回答
1
投票

正如我所说,如果我注释掉Id一切正常,但我不需要在UserDetails上使用PK

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

实际上,没有必要为Id实体提供UserDetails属性。那么为什么你的代码不起作用呢?原因是您将依赖实体和主体实体混为一谈。

HasForeignKey<>() method的原型是:

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);
});

看到了吗?在您的代码中,这里的entityUser,它是主要实体而不是从属实体。要解决此问题,请按以下方式更改代码:

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); 
    });
}

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

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对象资源管理器中似乎存在一个错误(请参阅my question here),导致我们无法看到已存在的FK禁区的错误。

但是,我们可以通过尝试插入具有不存在的UserId的行来证明它:

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

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

Msg 547,Level 16,State 0,Line 1

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

该语句已终止。

© www.soinside.com 2019 - 2024. All rights reserved.