web-dev-qa-db-fra.com

Identité à plusieurs utilisateurs - conception DbContext

J'essaie d'utiliser le package d'identité de .NET Core avec plusieurs classes qui étendent IdentityUser<Guid> mais avec une seule classe UserRole.

J'ai plusieurs classes qui s'étendent UserStore<T> pour chaque type d'utilisateur et une seule classe qui étend RoleStore<UserRole>.

Ce qui suit est mon 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>();

Mon DbContext ne s'étend pas IdentityDbContext:

public sealed class EntityDbContext: DbContext { }

J'obtenais plusieurs erreurs, j'ai donc ajouté ce qui suit à DbContext mais je l'ai commenté:

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

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

Je reçois de nombreuses erreurs différentes:

erreur de génération sur l'instance "Dal.IdentityStores.InternalUserStore" pour PluginType IUserStore - et l'instance "RoleManager" pour PluginType Microsoft.AspNetCore.Identity.RoleManager1[Models.Entities.Users.UserRole] - and Instance 'Dal.IdentityStores.GenericUserRoleStore' for PluginType Microsoft.AspNetCore.Identity.IRoleStore1 [Models.Entities.Users.UserRole] - et l'instance 'Dal.IdentityStores.GenericUserRoleStore' pour PluginType Microsoft.AspNetCore.Identity.IRoleStore1[Models.Entities.Users.UserRole] - and Instance 'Dal.IdentityStores.ContractorUserStore' for PluginType Microsoft.AspNetCore.Identity.IUserStore1 [Models.Entities.Contractors.Contractor] - et l'instance 'UserClaimsPrincipalFactory' pour PluginType Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory1[Models.Entities.Contractors.Contractor] - and Instance 'UserClaimsPrincipalFactory<Contractor, UserRole>' for PluginType Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory1 [Models.Entities.Contractors.Contractor] - et l'instance 'UserManager' pour PluginType Microsoft.AspNetCore.Identity.UserManager1[Models.Entities.Homeowners.Homeowner] - and Instance 'UserClaimsPrincipalFactory<Homeowner>' for PluginType Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory1 [Models.Entities.Homeowners.Homeowner]

Ceci est le lien vers mon dépôt

8
Node.JS

J'ai reproduit votre problème et voici une solution, mais je pense à nouveau à la création de plusieurs tables pour différents rôles d'utilisateur.

Voici deux raisons principales contre plusieurs tables d'utilisateurs:

  1. Lorsque vous souhaitez trouver l'utilisateur par id (en supposant que vous ne connaissez pas le rôle), vous devrez exécuter plusieurs requêtes sur différentes tables, ce qui diminue les performances et augmente la complexité du code.
  2. Cela peut également augmenter la complexité de la base de données, car vous devrez définir plusieurs clés étrangères vers d'autres tables.

Dans le cas où vous souhaitez toujours avoir plusieurs tables pour différents rôles d'utilisateur, voici un petit "hack". Vous avez juste besoin de remplacer la méthode OnModelCreating et de configurer les entités:

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

Après cela, vous devriez pouvoir vous connecter.

2
Vano Maisuradze

Sur la base des exigences expliquées par le PO dans les commentaires et du fait que le PO a déclaré qu'il n'a pas d'expérience en conception de bases de données, j'essaierai de fournir une réponse qui, je l'espère, facilitera la vie du projet et de la carrière de développeur de logiciels du PO:

Tout d'abord, vous voulez une seule table "Utilisateurs":

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

Vous n'avez aucune exigence pour plusieurs tables d'utilisateurs pour le projet, et aucun projet ou produit logiciel ne nécessiterait une telle chose d'une manière générale. Quand/IF une table utilisateur évolue à N milliards de documents, elle sera répartie sur plusieurs partitions pour les performances et/ou dénormalisée pour les lectures et les écritures, mais il s'agira logiquement d'une table dans de nombreuses conceptions de systèmes logiciels complexes à l'échelle.

Vous déclarez:

Comment puis-je avoir des métadonnées utilisateur différentes pour différents types d'utilisateurs? Un entrepreneur a ses propres propriétés, un propriétaire possède son propre ensemble de propriétés, etc. Je ne veux pas avoir une table géante avec toutes les propriétés possibles

Une de vos préoccupations est que vous ne vous retrouvez pas avec une table géante. Bref, ce n'est pas une préoccupation pour vous. Vous n'avez pas encore de table géante et si vous ajoutez 30 colonnes ("propriétés" comme vous l'avez dit), ce ne sera PAS une table géante.

Créez une seule table utilisateur. Ajoutez des colonnes nullables pour les propriétés spécifiques de l'entrepreneur et des colonnes nullables pour les propriétés spécifiques du propriétaire. Rendez les colonnes non nulles lorsque la propriété s'applique à tous les utilisateurs.

Une colonne qui s'appliquera à tous les utilisateurs est la colonne Rôle pour vos utilisateurs, vous avez donc une colonne Non Nullable appelée "Rôle"

Remarque, si vous utilisez une base de données relationnelle, vous n'avez pas besoin de créer une table de rôle qui contient les enregistrements de lignes de rôle. Vous pouvez le faire et votre professeur de cours peut s'y attendre, mais vous pouvez également créer une classe de chaînes de const. ou énumérer dans votre backend qui représente les rôles possibles et qui est une forme de persistance qui assure l'intégrité de ces rôles. Je mentionne cela parce que c'est un domaine où les développeurs passent rapidement en sur-intégrité référentielle avec la conception de bases de données relationnelles.

0
Brian Ogden