使用多个身份用户时没有类型 Identity.UserManager 的服务

No service for type Identity.UserManager when using multiple identity users

我的设置

目前,我有两个继承自 ApplicationUser 的模型,后者继承了 IdentityUser。用户 类 是:

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 { }

ApplicationUser 包含 共享 属性,例如名字和姓氏。 StudentUserEmployeeUser 都有自己的 属性和关系。此结构遵循 Table Per Hierarchy (TPH) 继承。

理想情况下,我想遵循Table Per Type (TPT)继承,因为SQL结构更好。 ASP.NET Core 仅原生支持 TPH,所以这就是我遵循 TPT 方法的原因。

问题

我在 Startup.cs 中添加了身份服务:

services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();

当我调用 UserManager<StudentUser>UserManager<EmployeeUser> 时,出现以下错误:

No service for type 'Microsoft.AspNetCore.Identity.UserManager`1[ClassroomMonitor.Models.StudentUser]' has been registered.

我的问题

不幸的是,我找不到太多关于此错误与此实现相结合的信息。

是否(甚至)可以让它以这种方式工作?

欢迎任何帮助或想法。

更新 1

手动添加 StudentUserEmployeeUser 作为作用域服务似乎不起作用(提到第一个 )。

services.AddScoped<UserManager<ApplicationUser>, UserManager<ApplicationUser>>();
// or..
services.AddScoped<UserManager<ApplicationUser>>();

这会引发以下错误:

InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Identity.IUserStore1[ClassroomMonitor.Models.StudentUser]'

更新 2

这里有一个 Gist 可以让您更好地了解项目结构:

您缺少它的寄存器 DI。

services.AddScoped<UserManager<AppUser>, UserManager<AppUser>>()

StudentUser和EmployeeUser类似

在新项目上测试:

dotnet new mvc --auth Individual

Startup.cshtml

services.AddDefaultIdentity<User>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

User.cs

public class User : IdentityUser
{
    public string Test { get; set; }
}

可能这是你的问题:

_LoginPartial.cshtml

@inject SignInManager<User> SignInManager
@inject UserManager<User> UserManager

也这样测试过:

Startup.cs

services.AddDefaultIdentity<User2>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

Users.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

理想情况下,您将为派生用户类型调用与基本用户类型相同的身份设置。

不幸的是,AddIdentity 方法包含一些阻止多次使用它的代码。

相反,您可以使用 AddIdentityCore. The role services are already registered by the AddIdentity, the only difference is that AddIdentityCore registers UserClaimsPrincipalFactory<TUser>, so in order to match AddIdentity setup it needs to be replaced with UserClaimsPrincipalFactory<TUser, TRole> via AddClaimsPrincipalFactory 方法。

代码看起来像这样:

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

当然你可以在自定义扩展方法中移动公共部分。

更新:虽然角色服务已经配置好,但您仍然需要调用AddRoles才能正确设置Role属性 的 IndentityBuilder,然后由 AddEntityFrameworkStores.

使用

你不能为不同的用户类型添加作用域,你真的不应该有许多从 IdentityUser 派生的不同类型,因为这意味着你要么在整个地方使用不正确的类型,要么在数据库。

你真的应该构建你的数据,所以它有一个 ApplicationUser 被员工实体(一个单独的实体)或学生实体(再次是一个单独的实体)引用。 这里更多的是设计问题而不是代码问题。

ASPIdentity 将在您 AddIdentity<ApplicationUser, IdentityRole> 时注入 Usermanager<ApplicationUser>,因此添加更多用户管理器将无法按预期工作。

答案 您必须为每种类型的用户创建不同的实体,1 个用于学生,1 个用于员工等。这些都将有一个指向用户 ID 的外键,这将使您能够添加多种类型的用户而无需不同的表每个用户类型。

然后当你 addIdentity 你可以注册 ApplicationUser (就像现在一样)然后注入 UserManager<ApplicationUser> 这将获得用户类型。

如果核心>=3.0

services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<WorldContext>();