Пользовательский UserManager всегда возвращает null

Я пытаюсь создать свой собственный UserManager, расширяющий оригинал, и когда я выполняю поиск по электронной почте, пользователь не найден. Но если я делаю поиск из контекста, если я нахожу пользователя (см. метод Get). Чтобы убедиться, что он действительно хорошо реализован, я перезаписал метод FindByEmailAsync и он действительно вызывается, но я не знаю, почему пользователь не может его найти. Немного помощи? Благодарю вас!

public void ConfigureServices(IServiceCollection servicesCollection)
{
    servicesCollection.AddDbContext<MyIndentityContext>(currentOptions =>
        currentOptions.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    servicesCollection.AddIdentity<ApplicationUser, ApplicationRole>()
        .AddEntityFrameworkStores<MyIndentityContext>()
        .AddRoleStore<ApplicationRoleStore>()
        .AddUserStore<ApplicationUserStore>()
        .AddUserManager<ApplicationUserManager>()
        .AddRoleManager<ApplicationRoleManager>()
        .AddSignInManager<ApplicationSignInManager>()
        .AddDefaultTokenProviders();

        ...

        ...

        ...
}

public class MyIndentityContext : IdentityDbContext<ApplicationUser, ApplicationRole, string>
{
    private readonly IConfiguration _configuration;

    private readonly IHttpContextAccessor _httpContextAccessor;

    public MyIndentityContext(DbContextOptions dbContextOptions, IHttpContextAccessor httpContextAccessor,
        IConfiguration configuration)
        : base(dbContextOptions)
    {
        _configuration = configuration;
        _httpContextAccessor = httpContextAccessor;
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.HasDefaultSchema("Sample.API");
    }

}

public class ApplicationRoleManager : RoleManager<ApplicationRole>
{
    public ApplicationRoleManager(IRoleStore<ApplicationRole> roleStore,
        IEnumerable<IRoleValidator<ApplicationRole>> roleValidators, ILookupNormalizer keyNormalizer,
        IdentityErrorDescriber errors, ILogger<ApplicationRoleManager> logger) : base(roleStore,
        roleValidators,
        keyNormalizer, errors, logger)
    {
    }
}

public class ApplicationSignInManager : SignInManager<ApplicationUser>
{
    public ApplicationSignInManager(UserManager<ApplicationUser> userManager, IHttpContextAccessor contextAccessor,
        IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory, IOptions<IdentityOptions> optionsAccessor,
        ILogger<ApplicationSignInManager> logger, IAuthenticationSchemeProvider schemes) : base(userManager,
        contextAccessor, claimsFactory, optionsAccessor, logger, schemes)
    {
    }
}

public class ApplicationUserManager : UserManager<ApplicationUser>
{
    public ApplicationUserManager(IUserStore<ApplicationUser> userStore, IOptions<IdentityOptions> optionsAccessor,
        IPasswordHasher<ApplicationUser> passwordHasher,
        IEnumerable<IUserValidator<ApplicationUser>> userValidators,
        IEnumerable<IPasswordValidator<ApplicationUser>> passwordValidators, ILookupNormalizer keyNormalizer,
        IdentityErrorDescriber errors, IServiceProvider services, ILogger<ApplicationUserManager> logger) :
        base(userStore, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors,
            services, logger)
    { }

    // Custom implementation to check if you are really calling the method
    public override Task<ApplicationUser> FindByEmailAsync(string email)
    {
        return Task.Run(() => new ApplicationUser
        {
            UserName = "A_NAME"
        });
    }
}

public class ApplicationRoleStore : RoleStore<ApplicationRole, MyIndentityContext>
{
    public ApplicationRoleStore(MyIndentityContext dbContext, IdentityErrorDescriber identityErrorDescriber)
        : base(dbContext, identityErrorDescriber)
    {}
}

public class ApplicationUserStore : UserStore<ApplicationUser, ApplicationRole, MyIndentityContext, string>
{
    public ApplicationUserStore(MyIndentityContext dbContext, IdentityErrorDescriber identityErrorDescriber)
        : base(dbContext, identityErrorDescriber)
    {}

}

public class ApplicationUser : IdentityUser {}

public class ApplicationRole : IdentityRole
{
    public ApplicationRole() { }

    public ApplicationRole(string roleName) : base(roleName) { }

    public ApplicationRole(string roleName, string roleDescription) : base(roleName)
    {
        Description = roleDescription;
    }

}

[Authorize]
[ApiController]
[Route("api/[controller]")]
[EnableCors(CORS.AllowSpecificOrigins)]

public class UserController : BaseController
{
    private readonly ApplicationUserManager _applicationUserManager;

    public UserController(ApplicationUserManager applicationUserManager)
    {
        _applicationUserManager = applicationUserManager;
    }

     // GET: api/User/5
    [HttpGet("{id}")]
    public async Task<UserDTO> Get(int id)
    {

            var currentUser = await _applicationUserManager.FindByEmailAsync("[email protected]"); ==> RETURN NULL!

            var otherUser = _indentityContext.Users.Where(x => x.Email == "[email protected]"); ==> RETURN CORRECT USER!

            return currentUser;

    }

}

person avechuche    schedule 25.07.2018    source источник
comment
Если вы еще не видели Пользовательский поставщики хранилища для ASP.NET Core Identity, которые могут дать вам некоторые подсказки.   -  person Mark G    schedule 25.07.2018
comment
return Task.Run(...) никогда не будет работать. Я предполагаю, что это всего лишь пример кода, но попробуйте return Task.FromResult(new ApplicationUser { UserName = "A_NAME" });, чтобы он заработал.   -  person Brad    schedule 26.07.2018
comment
Не могли бы вы поделиться с нами проектом? Я сделал тест с вашим текущим кодом, он работает правильно. Вы используете несколько DbContext? Вы получаете пользователя Task.Run(() => new ApplicationUser { UserName = "A_NAME" });?   -  person Edward    schedule 27.07.2018
comment
@ Эдвард Привет! Я использую только один контекст. Я добавил Task.Run, чтобы убедиться, что метод FindByEmailAsync действительно вызывается. Это возвращает пользователя с именем A_NAME. Позже я оставлю ссылку на github с кодом.   -  person avechuche    schedule 27.07.2018
comment
@Edward Проверьте репозиторий. github.com/avechuche/Sample.API.git   -  person avechuche    schedule 28.07.2018


Ответы (1)


Примечание. Этот ответ ссылается на код и значения, показанные в репродукции Github.

Когда вы вызываете UserManager.FindByEmailAsync, значение, которое вы передаете в метод, нормализуется — по умолчанию эта нормализация преобразует значение в верхний регистр. Затем это нормализованное значение используется для поиска в столбце NormalizedEmail в таблице AspNetUsers.

Внутри вашего метода MyIndentityContext.OnModelCreating у вас есть следующий код:

modelBuilder.Entity<ApplicationUser>().HasData(
    new ApplicationUser
    {
        Email = "[email protected]"
    });

Поскольку здесь вы берете под свой контроль данные и устанавливаете только Email, значение NormalizedEmail в базе данных не устанавливается (это null). Это означает, что когда вы используете UserManager.FindByEmailAsync и ищете [email protected] в столбце NormalizedEmail, совпадений нет. Однако если вы воспользуетесь DbSet напрямую и посмотрите на столбец Email, вы можете найти соответствующую запись для [email protected].

Чтобы решить эту проблему, я рекомендую вместо использования HasData для заполнения ваших пользователей (пользователей) использовать метод UserManager.CreateAsync внутри метода заполнения в вашем приложении. Это гарантирует, что нормализация и другая связанная обработка будут происходить должным образом до того, как записи будут сохранены в базе данных.

person Kirk Larkin    schedule 28.07.2018