Ma configuration
Actuellement, j'ai deux modèles qui héritent de ApplicationUser
, qui est un type IdentityUser
. Les classes d'utilisateurs sont:
public abstract class ApplicationUser : IdentityUser
{
[PersonalData]
public string FirstName { get; set; }
[PersonalData]
public string LastName { get; set; }
[NotMapped]
public string FullName => $"{FirstName} {LastName}";
}
public class StudentUser : ApplicationUser
{
[PersonalData]
[Required]
public string StudentNumber { get; set; }
// A user belongs to one group
public Group Group { get; set; }
}
public class EmployeeUser : ApplicationUser { }
La ApplicationUser
contient les propriétés partagées, comme le prénom et le nom. StudentUser
et EmployeeUser
ont tous les deux leurs propriétés et leurs relations own. Cette structure suit l'héritage Table par hiérarchie (TPH) .
Idéalement, je veux suivre l'héritage Table par type (TPT) , car la structure SQL serait meilleure. .NET Core ne supporte que TPH de manière native, c'est pourquoi je suis cette approche.
Le problème
Dans Startup.cs
j'ai ajouté le service d'identité:
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
Lorsque j'appelle UserManager<StudentUser>
ou UserManager<EmployeeUser>
, j'obtiens le message d'erreur suivant:
Aucun service pour le type 'Microsoft.AspNetCore.Identity.UserManager`1 [ClassroomMonitor.Models.StudentUser]' n'a été enregistré.
Ma question
Malheureusement, je ne trouve pas grand chose à propos de cette erreur dans cette implémentation. Est-il possible (même) de le faire fonctionner de cette façon?
Toute aide ou pensées sont les bienvenues.
Mise à jour 1
L'ajout manuel de StudentUser
ou EmployeeUser
en tant que services étendus ne semble pas fonctionner (mentionné en tant que premier answer ).
services.AddScoped<UserManager<ApplicationUser>, UserManager<ApplicationUser>>();
// or..
services.AddScoped<UserManager<ApplicationUser>>();
Cela lève l'erreur suivante:
InvalidOperationException: impossible de résoudre le service pour le type 'Microsoft.AspNetCore.Identity.IUserStore1 [ClassroomMonitor.Models.StudentUser]'
Mise à jour 2
Voici un Gist pour vous donner une meilleure image de la structure du projet:
Idéalement, vous appelez la même configuration d'identité pour les types d'utilisateurs dérivés que pour le type d'utilisateurs de base.
Malheureusement, AddIdentity
method contient du code qui l’empêche de l’utiliser plusieurs fois.
Au lieu de cela, vous pouvez utiliser AddIdentityCore
. Les services de rôle sont déjà enregistrés par la variable AddIdentity
. La seule différence est que AddIdentityCore
enregistre UserClaimsPrincipalFactory<TUser>
et que, pour correspondre à la configuration AddIdentity
, il doit être remplacé par UserClaimsPrincipalFactory<TUser, TRole>
via AddClaimsPrincipalFactory
méthode.
Le code ressemble à quelque chose comme ça:
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders()
.AddDefaultUI();
services.AddIdentityCore<StudentUser>()
.AddRoles<IdentityRole>()
.AddClaimsPrincipalFactory<UserClaimsPrincipalFactory<StudentUser, IdentityRole>>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders()
.AddDefaultUI();
services.AddIdentityCore<EmployeeUser>()
.AddRoles<IdentityRole>()
.AddClaimsPrincipalFactory<UserClaimsPrincipalFactory<EmployeeUser, IdentityRole>>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders()
.AddDefaultUI();
Bien sûr, vous pouvez déplacer les parties communes dans une méthode d'extension personnalisée.
Update: Bien que les services de rôle soient déjà configurés, vous devez toujours appeler AddRoles
afin de définir correctement la propriété Role
de IndentityBuilder
, qui est ensuite utilisée par AddEntityFrameworkStores
.
Il vous manque le registre DI pour cela.
services.AddScoped<UserManager<AppUser>, UserManager<AppUser>>()
StudentUser et EmployeeUser lui ressemblent
Testé sur un nouveau projet:
dotnet new mvc --auth Individual
Startup.cshtml
services.AddDefaultIdentity<User>()
.AddEntityFrameworkStores<ApplicationDbContext>();
Utilisateur.cs
public class User : IdentityUser
{
public string Test { get; set; }
}
Probablement voici votre problème:
_LoginPartial.cshtml
@inject SignInManager<User> SignInManager
@inject UserManager<User> UserManager
Également testé de cette façon:
Startup.cs
services.AddDefaultIdentity<User2>()
.AddEntityFrameworkStores<ApplicationDbContext>();
Utilisateurs.cs
public class User : IdentityUser
{
public string TestA { get; set; }
}
public class User2 : User
{
public string TestB { get; set; }
}
_LoginPartial.cshtml
@inject SignInManager<User2> SignInManager
@inject UserManager<User2> UserManager
Vous ne pouvez pas ajouter d’étendue pour les différents types d’utilisateur, vous ne devriez pas avoir beaucoup de types différents dérivés d’IdentityUser, cela voudrait dire que vous aurez soit des types incorrects utilisés partout ou plusieurs tables d’utilisateur différentes dans la base de données.
vous devriez vraiment structurer vos données de manière à ce qu'elles aient une variable ApplicationUser
qui soit référencée par l'entité employé (une entité distincte) ou l'entité Étudiant (encore une fois une entité distincte) . .
ASPIdentity injectera le Usermanager<ApplicationUser>
lorsque vous AddIdentity<ApplicationUser, IdentityRole>
, de sorte que l'ajout de plusieurs gestionnaires d'utilisateurs ne fonctionnera pas comme prévu.
La réponse vous devez créer différentes entités pour chaque type d'utilisateur, 1 pour l'étudiant, 1 pour l'employé, etc. Elles auront toutes une clé étrangère associée à l'ID de l'utilisateur, ce qui vous permettra d'ajouter plusieurs types d'utilisateur sans avoir besoin de tables différentes. par type d'utilisateur.
alors quand vous addIdentity
vous pourrez enregistrer ApplicationUser
(comme c'est le moment) puis injectez UserManager<ApplicationUser>
qui obtiendra le type d'utilisateur.