Нет службы для типа Identity.UserManager при использовании нескольких пользователей удостоверений

Моя настройка

В настоящее время у меня есть две модели, которые наследуются от 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 содержит общие свойства, такие как имя и фамилия. И StudentUser и EmployeeUser имеют свои собственные свойства и отношения. Эта структура следует наследованию таблицы на иерархию (TPH).

В идеале я хочу следовать наследованию таблиц по типу (TPT), потому что структура SQL лучше. ASP.NET Core изначально поддерживает только TPH, поэтому я использую подход TPT.

Эта проблема

Я добавил службу идентификации в Startup.cs:

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

Когда я вызываю UserManager<StudentUser> или UserManager<EmployeeUser>, я получаю следующую ошибку:

Служба для типа "Microsoft.AspNetCore.Identity.UserManager'1 [ClassroomMonitor.Models.StudentUser]" не зарегистрирована.

Мой вопрос

К сожалению, я не могу найти много об этой ошибке в сочетании с этой реализацией.

Возможно ли (даже) заставить это работать таким образом?

Любая помощь или мысли приветствуются.

Обновление 1

StudentUser добавление StudentUser или EmployeeUser в качестве сервисов с определенной областью не работает (упоминается в качестве первого ответа).

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

Это приводит к следующей ошибке:

InvalidOperationException: невозможно разрешить службу для типа 'Microsoft.AspNetCore.Identity.IUserStore1 [ClassroomMonitor.Models.StudentUser]'

Обновление 2

Вот Гист, чтобы дать вам лучшее представление о структуре проекта:

Ответ 1

В идеале вы бы назвали одну и ту же настройку идентификации для производных типов пользователей, как и для базового типа пользователя.

К сожалению, метод AddIdentity содержит код, который предотвращает его использование более одного раза.

Вместо этого вы можете использовать AddIdentityCore. Службы роли уже зарегистрированы с помощью AddIdentity, единственное отличие состоит в том, что AddIdentityCore регистрирует UserClaimsPrincipalFactory<TUser>, поэтому для соответствия настройке AddIdentity его необходимо заменить на UserClaimsPrincipalFactory<TUser, TRole> через метод 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.

Ответ 2

Вам не хватает регистра DI для этого.

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

StudentUser и EmployeeUser похожи на него

Ответ 3

Протестировано по новому проекту:

dotnet new mvc --auth Индивидуальный

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

Ответ 4

Вы не можете добавлять области для разных типов пользователей, у вас должно быть не так много разных типов, которые производны от IdentityUser, так как это будет означать, что вы будете либо иметь неправильные типы, используемые по всему месту, либо несколько разных пользовательских таблиц в базе данных.

вы должны структурировать свои данные таким образом, чтобы у него был ApplicationUser который ссылается объект-сотрудник (отдельный объект) или объект-студент (опять-таки отдельный объект). здесь больше проблемы с дизайном, чем проблема с кодом.

ASPIdentity будет вводить Usermanager<ApplicationUser> когда вы AddIdentity<ApplicationUser, IdentityRole>, поэтому добавление большего количества менеджеров пользователей не будет работать AddIdentity<ApplicationUser, IdentityRole> образом.

Ответ: вы должны создавать разные сущности для каждого типа пользователей, 1 для студентов, 1 для сотрудников и т.д., Все они будут иметь внешний ключ для идентификатора пользователя, и это позволит вам добавлять несколько типов пользователей без необходимости разные таблицы для каждого типа пользователя.

то когда вы addIdentity вы можете зарегистрировать ApplicationUser (как сейчас), а затем ввести UserManager<ApplicationUser> который получит тип пользователя.