Identidade de tipo de usuário múltiplo - design do DbContext

8

Estou tentando usar o pacote de identidade do .NET Core com várias classes que se estendem, IdentityUser<Guid>mas com uma única UserRoleclasse.

Eu tenho várias classes que se estendem UserStore<T>para cada tipo de usuário e uma única classe que se estende RoleStore<UserRole>.

O seguinte é o meu startup.cs:

services.AddIdentity<InternalUser, UserRole>(IdentityOptions)
    .AddDefaultTokenProviders()
    .AddUserStore<InternalUserStore>()
    .AddRoleStore<GenericUserRoleStore>();

services.AddIdentityCore<Contractor>(IdentityOptions)
    .AddRoles<UserRole>()
    .AddDefaultTokenProviders()
    .AddUserStore<ContractorUserStore>()
    .AddRoleStore<GenericUserRoleStore>();

services.AddIdentityCore<Homeowner>(IdentityOptions)
    .AddRoles<UserRole>()
    .AddDefaultTokenProviders()
    .AddUserStore<HomeownerUserStore>()
    .AddRoleStore<GenericUserRoleStore>();

Meu DbContextnão está estendendo IdentityDbContext:

public sealed class EntityDbContext: DbContext { }

Eu estava recebendo vários erros, então adicionei o seguinte, DbContextmas comentei:

public DbSet<IdentityUserClaim<Guid>> UserClaims { get; set; }

public DbSet<IdentityUserRole<Guid>> UserRoles { get; set; }

Estou recebendo muitos erros diferentes:

erro de compilação na Instância 'Dal.IdentityStores.InternalUserStore' para PluginType IUserStore - e Instância 'RoleManager' para PluginType Microsoft.AspNetCore.Identity.RoleManager 1[Models.Entities.Users.UserRole] - and Instance 'Dal.IdentityStores.GenericUserRoleStore' for PluginType Microsoft.AspNetCore.Identity.IRoleStore1 [Models.Entities.Users.UserRole] - e Instance 'Dal.IdentityStores.GenericUserStore. PluginType Microsoft.AspNetCore.Identity.IRoleStore 1[Models.Entities.Users.UserRole] - and Instance 'Dal.IdentityStores.ContractorUserStore' for PluginType Microsoft.AspNetCore.Identity.IUserStore1 [Models.Entities.Contractors.Contractor] - e a Instância 'UserClaimsPrincipalFactory' para PluginType Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory 1[Models.Entities.Contractors.Contractor] - and Instance 'UserClaimsPrincipalFactory<Contractor, UserRole>' for PluginType Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory1 [Models.Entities.Contractors 'forManager] - PluginType Microsoft.AspNetCore.Identity.UserManager 1[Models.Entities.Homeowners.Homeowner] - and Instance 'UserClaimsPrincipalFactory<Homeowner>' for PluginType Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory1 [Models.Entities.Homeowners.Homeowner]

Este é o link para o meu repo

Node.JS
fonte
Então, qual é a sua pergunta?
Vano Maisuradze
Meu código não funciona. Estou recebendo o erro acima. Como posso corrigi-lo de maneira a usar vários tipos como um "Usuário".
Node.js
Eu clonei seu repositório e funcionou para mim, então provavelmente é um problema de ambiente.
Vano Maisuradze
1
@VanoMaisuradze Tente criar usuário e entrar. Você verá o problema. Existe um controlador para registro / login
Node.JS
1
Sim eu fiz. Obrigado
Node.JS

Respostas:

2

Reproduzi seu problema e a seguir há uma solução para ele, mas gostaria de pensar novamente sobre a criação de várias tabelas para diferentes funções de usuário.

Aqui estão duas razões principais para várias tabelas de usuários:

  1. Quando você deseja localizar o usuário por ID (supondo que você não conheça a função), será necessário executar várias consultas em tabelas diferentes, o que diminui o desempenho e aumenta a complexidade do código.
  2. Também pode aumentar a complexidade do banco de dados, porque você precisará definir várias chaves estrangeiras para outras tabelas.

Caso você ainda queira ter várias tabelas para diferentes funções de usuário, aqui está um pequeno "hack". Você só precisa substituir o método OnModelCreating e configurar entidades:

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

        builder.Entity<Contractor>(b =>
        {
            b.HasMany<IdentityUserRole<Guid>>().WithOne().HasForeignKey(ur => ur.UserId).IsRequired();
        });

        builder.Entity<UserRole>(b =>
        {
            b.HasKey(r => r.Id);
            b.HasIndex(r => r.NormalizedName).HasName("RoleNameIndex").IsUnique();
            b.ToTable("AspNetRoles");
            b.Property(r => r.ConcurrencyStamp).IsConcurrencyToken();

            b.Property(u => u.Name).HasMaxLength(256);
            b.Property(u => u.NormalizedName).HasMaxLength(256);

            b.HasMany<IdentityUserRole<Guid>>().WithOne().HasForeignKey(ur => ur.RoleId).IsRequired();
            b.HasMany<IdentityRoleClaim<Guid>>().WithOne().HasForeignKey(rc => rc.RoleId).IsRequired();
        });

        builder.Entity<IdentityRoleClaim<Guid>>(b =>
        {
            b.HasKey(rc => rc.Id);
            b.ToTable("AspNetRoleClaims");
        });

        builder.Entity<IdentityUserRole<Guid>>(b =>
        {
            b.HasKey(r => new { r.UserId, r.RoleId });
            b.ToTable("AspNetUserRoles");
        });

        builder.Entity<UserRole>().ToTable("Roles");
        builder.Entity<IdentityUserRole<Guid>>().ToTable("UserRoles");
        builder.Entity<IdentityRoleClaim<Guid>>().ToTable("RoleClaims");
        builder.Entity<IdentityUserClaim<Guid>>().ToTable("UserClaims");
    }

Depois disso, você deverá conseguir fazer o login.

Vano Maisuradze
fonte
0

Com base nos requisitos que o OP explicou nos comentários e que o OP disse que não possui experiência em design de banco de dados, tentarei fornecer uma resposta que, espero, ajude a facilitar a vida da carreira de desenvolvimento de software e projeto do OP:

Primeiro, você deseja uma única tabela "Usuários":

|---------------------|------------------|------------------|
|      FirstName      |     LastName     |       Role       |
|---------------------|------------------|------------------|
|          Joe        |       Black      |     Contractor   |
|---------------------|------------------|------------------|

Você não tem requisitos para várias tabelas de usuário para o projeto, nem qualquer projeto ou produto de software exigiria isso em termos gerais. Quando / SE uma tabela Usuário é escalada para N número bilhões de registros, ela seria dividida entre partições para desempenho e / ou desnormalizada para leituras versus gravações, mas ainda assim seria logicamente uma tabela em muitos projetos de sistemas de software complexos em escala.

Você declara:

Como posso ter metadados de usuário diferentes para diferentes tipos de usuário? Um empreiteiro tem suas próprias propriedades, um proprietário tem seu próprio conjunto de propriedades e etc. Não quero ter uma mesa gigante com todas as propriedades possíveis

Uma preocupação sua é que você não termine com uma mesa gigante. Em suma, isso não é uma preocupação para você. Você ainda não tem uma mesa gigante e, se adicionar 30 colunas ("propriedades", como disse), essa NÃO seria uma mesa gigante.

Crie uma única tabela de usuário. Adicione colunas anuláveis ​​para as propriedades específicas do contratante e colunas anuláveis ​​para as propriedades específicas do proprietário. Torne as colunas Not Null quando a propriedade se aplicar a todos os usuários.

Uma coluna que se aplicará a todos os usuários é a coluna Função para seus Usuários, para que você tenha uma coluna Não Anulável chamada "Função"

Observe que, se você estiver usando um banco de dados relacional, não precisará criar uma tabela de Função que contenha os registros de linha de Função. Você pode fazer isso e o instrutor do curso pode esperar, mas também pode criar uma classe const ou enum em seu back-end que represente as possíveis funções e que seja uma forma de persistência que forneça integridade para essas funções. Menciono isso porque essa é uma área em que os desenvolvedores rapidamente entram em exagero na integridade referencial com o design de banco de dados relacional.

Brian Ogden
fonte
1
Desculpe, mas sua resposta é uma sugestão, que eu já fiz. Isso não responde diretamente à pergunta do OP. Também criar colunas anuláveis ​​pode não ser uma boa solução. Por exemplo, usar Json pode ser muito mais limpo.
Vano Maisuradze
Não, é um pouco mais do que uma sugestão, é uma maneira correta e educada de abordar e implementar a solução.
Brian Ogden