Identidad de tipo de usuario múltiple: diseño de DbContext

8

Estoy tratando de usar el paquete Identity de .NET Core con múltiples clases que se extienden IdentityUser<Guid>pero con una sola UserRoleclase.

Tengo varias clases que se extienden UserStore<T>para cada tipo de usuario y una sola clase que se extiende RoleStore<UserRole>.

El siguiente es mi 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>();

Mi DbContextno se extiende IdentityDbContext:

public sealed class EntityDbContext: DbContext { }

Estaba recibiendo múltiples errores, así que agregué lo siguiente DbContextpero lo comenté:

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

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

Recibo muchos errores diferentes:

Error de compilación en la instancia 'Dal.IdentityStores.InternalUserStore' para PluginType IUserStore - e Instancia '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.Reneric Store' 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 Instancia '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.Contractor.Contraager.Contractor.Contractor.Instalador ' 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 es el enlace a mi repositorio

Node.JS
fuente
Entonces, ¿cuál es tu pregunta?
Vano Maisuradze
Mi código no funciona Recibo el error anterior. ¿Cómo puedo solucionarlo de tal manera que use varios tipos como "Usuario"?
Nodo.JS
He clonado su repositorio y funcionó para mí, así que probablemente sea su problema medioambiental.
Vano Maisuradze
1
@VanoMaisuradze Intenta crear usuarios e iniciar sesión. Verás el problema. Hay un controlador para registrarse / iniciar sesión
Node.JS
1
Sí, lo hice. Gracias
Node.JS

Respuestas:

2

Reproduje su problema y a continuación hay una solución, pero volvería a pensar en crear varias tablas para diferentes roles de usuario.

Aquí hay dos razones principales contra múltiples tablas de usuario:

  1. Cuando desee encontrar al usuario por id (suponiendo que no conozca el rol), deberá ejecutar múltiples consultas en diferentes tablas, lo que disminuye el rendimiento y aumenta la complejidad del código.
  2. También puede aumentar la complejidad de la base de datos, ya que necesitará configurar varias claves externas para otras tablas.

En caso de que aún desee tener varias tablas para diferentes roles de usuario, aquí hay un pequeño "truco". Solo necesita anular el método OnModelCreating y 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");
    }

Después de eso, deberías poder iniciar sesión.

Vano Maisuradze
fuente
0

Basado en los requisitos que el OP explicó en los comentarios y que el OP dijo que él / ella no tiene experiencia en el diseño de bases de datos, intentaré proporcionar una respuesta que con suerte ayude a facilitar la vida del proyecto de OP y la carrera de desarrollo de software:

En primer lugar, desea una sola tabla de "Usuarios":

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

No tiene requisitos para múltiples tablas de usuario para el proyecto, ni ningún proyecto o producto de software requerirá tal cosa en términos generales. Cuando / SI una tabla de usuario se escala a un número N de miles de millones de registros, se dividiría entre particiones para el rendimiento y / o se normalizaría para lecturas frente a escrituras, pero lógicamente seguiría siendo una tabla en muchos diseños complejos de sistemas de software escalados.

Usted declara:

¿Cómo puedo tener diferentes metadatos de usuario para diferentes tipos de usuario? Un contratista tiene sus propias propiedades, un propietario tiene su propio conjunto de propiedades, etc. No quiero tener una mesa gigante con todas las propiedades posibles.

Una preocupación tuya es que no terminas con una mesa gigante. En resumen, esto no es una preocupación para usted. Aún no tiene una tabla gigante y si agrega 30 columnas ("propiedades" como dijo) esa NO sería una tabla gigante.

Crear una sola tabla de usuario. Agregue columnas anulables para las propiedades específicas del contratista y columnas anulables para las propiedades específicas del propietario. Haga que las columnas no sean nulas cuando la propiedad se aplique a todos los usuarios.

Una columna que se aplicará a todos los usuarios es la columna Rol para sus usuarios, por lo que tiene una columna No anulable llamada "Rol"

Tenga en cuenta que si está utilizando una base de datos relacional, no necesita crear una tabla de roles que contenga los registros de las filas de roles. Puede hacer esto y su instructor del curso puede esperarlo, pero también puede crear una clase o enumeración de cadenas constantes en su backend que represente los posibles roles y que sea una forma de persistencia que brinde integridad para esos roles. Menciono esto porque esta es un área donde los desarrolladores entran rápidamente en la integridad referencial con el diseño de bases de datos relacionales.

Brian Ogden
fuente
1
Lo siento, pero su respuesta es una sugerencia que ya hice. Esto no responde directamente a la pregunta de OP. También crear columnas anulables puede no ser una buena solución. Por ejemplo, usar Json podría ser mucho más limpio.
Vano Maisuradze
No, es un poco más que una sugerencia, es una forma correcta y cortés de abordar e implementar la solución.
Brian Ogden el