Невозможно определить таблицу без 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 реализовать FK на 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 я получаю эту ошибку:

The entity type 'UserDetails' requires a primary key to be defined.

Как я уже сказал, если я закомментировал Id, все работает хорошо, но мне не нужен PK на UserDetails.

Если есть только один UserDetails на User, не является ли UserId первичным ключом?

Jacob Krall 13.09.2018 21:28

Вам этот первичный ключ не нужен, а вот EF6 нужен :) Он ничего не ломает, пусть будет автоинкремент. Тем не менее, я не понимаю, зачем вам нужна еще одна таблица для точного сопоставления данных от пользователя к деталям.

Yeldar Kurmangaliyev 13.09.2018 21:29

@JacobKrall да, я указал key внутри OnModelCreating, возможно, я что-то упустил? А для @Yeldar это просто организация

Charanoglu 13.09.2018 21:31

@Charanoglu Якоб, вероятно, имел в виду сделать UserId первичным ключом UserDetails в дополнение к тому, что он был внешним ключом.

ckuri 13.09.2018 21:43

@ckuri Как я могу это сделать? Разве EF не должен распознавать идентификатор, связанный с именем свойства?

Charanoglu 13.09.2018 21:45

Это должно быть builder.Entity<UserDetails>().HasKey(entity => entity.UserId) или атрибут [Key] выше UserId.

ckuri 13.09.2018 21:55

@ckuri, подождите, как можно узнать, что таблица AspNetUsers связывает UserId с UserDetails, если в вашем коде вы указали только UserDetails?

Charanoglu 13.09.2018 21:59

Вы уже расширяете класс IdentityUser, так почему бы просто не поместить свойства UserDetails в класс User. Вы чрезмерно усложняете свою модель.

Brad 14.09.2018 01:25
2
8
73
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

As I said if I commented out the Id all works well, but I don't need a PK on UserDetails

Если я правильно понимаю, вы просто не хотите добавлять дополнительный столбец (например, Id или UserDetailsId), установленный в качестве ПК. Таблица UserDetails, которую вы хотите создать, будет иметь UserId, который ссылается на таблицу User, и UserId будет одновременно служить PK и FK.

Фактически, нет необходимости иметь свойство 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 здесь - это User, который является основным объектом, а не зависимым объектом. Чтобы исправить это, измените свой код, как показано ниже:

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

Кстати, в обозревателе объектов SQL Server Visual Studio (см. мой вопрос здесь), похоже, есть ошибка, в результате которой мы не видим уже существующих ограничений 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

The INSERT statement conflicted with the FOREIGN KEY constraint "FK_UserDetail_AspNetUsers_UserId". The conflict occurred in database "App-EFCore-FK-Test", table "dbo.AspNetUsers", column 'Id'.

The statement has been terminated.

Другие вопросы по теме