UserManager.FindByEmailAsync возвращает значение null, но пользователь существует в базе данных

UserManager.FindByEmailAsync возвращает null, но пользователь существует в базе данных.

Код ниже объясняет странную проблему:

var email = info.Principal.FindFirstValue(ClaimTypes.Email);
var test = new Data.ApplicationDbContext().Users.First(x => x.NormalizedEmail == email);
var usermail = await _userManager.FindByEmailAsync(email);

Console.WriteLine(test == null);      //false
Console.WriteLine(usermail == null);  //true

РЕДАКТИРОВАТЬ

Также через сам _userManager получается нужный пользователь:

var test = _userManager.Users.FirstOrDefault(x => x.NormalizedEmail == email);
var usermail = await _userManager.FindByEmailAsync(email);

Console.WriteLine(test == null);      //false
Console.WriteLine(usermail == null);  //true

Следует отметить, что пользователь был создан не обычным способом, а с помощью Data-Seed (в OnModelCreating):

protected override void OnModelCreating(ModelBuilder builder)
{
    var users = new (string email, string name)[] {
        ("[email protected]", "admin")
    };

    var appUsers = users.Select(x => new ApplicationUser
    {
        Email = x.email,
        NormalizedEmail = x.email,
        NormalizedUserName = x.email,
        UserName = x.email,
        EmailConfirmed = true,
        Id = Guid.NewGuid().ToString(),
        SecurityStamp = Guid.NewGuid().ToString()
    }).ToArray();

    var role = new IdentityRole("Admins") { Id = Guid.NewGuid().ToString() };
    var roleToUser = appUsers.Select(x => new IdentityUserRole<string> { RoleId = role.Id, UserId = x.Id });

    builder.Entity<ApplicationUser>().HasData(appUsers);
    builder.Entity<IdentityRole>().HasData(role);
    builder.Entity<IdentityUserRole<string>>().HasData(roleToUser);
        
    base.OnModelCreating(builder);
}

person dovid    schedule 14.01.2019    source источник
comment
Вы не сказали, чего на самом деле хотите? Вы просто представили код и ничего не сказали!   -  person TanvirArjel    schedule 14.01.2019
comment
@TanvirArjel Можете ли вы указать на недостающую информацию после заголовка и кода? Я ожидаю, что (await _userManager.FindByEmailAsync(email)) == new Data.ApplicationDbContext().Users.First(x => x.NormalizedEmail == email);   -  person dovid    schedule 14.01.2019
comment
var email = info.Principal.FindFirstValue(ClaimTypes.Email); возвращает адрес электронной почты, который используется для внешнего входа. Итак, вы создали пользователя с этим адресом электронной почты?   -  person TanvirArjel    schedule 14.01.2019
comment
Не могли бы вы добавить свой ApplicationDbContext() код?   -  person TanvirArjel    schedule 14.01.2019
comment
Я проверил FindByEmailAsync, который вызывает FirstOrDefaultAsync на наборе Users точно так же, как и вы. Единственная разница между вашим запросом и тем, что делает UserStore, - это строка 1414, но это можно игнорировать, поскольку FindByEmail возвращает вам null.   -  person B12Toaster    schedule 15.01.2019
comment
Следовательно: действительно ли Users, установленный в DbContext, связан с классом User, зарегистрированным с IdentityUser? Я имею в виду, что, возможно, _userManager работает с совершенно другим набором баз данных или работает с InMemoryUserStore?   -  person B12Toaster    schedule 15.01.2019
comment
Для лучшей отладки не могли бы вы сравнить dbContext.Users.Count() с _userManager.Users.Count(), чтобы увидеть, отклоняются ли они? Или выполните свой запрос в наборе, управляемом UserManager: _userManager.Users.First(x => x.NormalizedEmail == email);, и проверьте, дает ли это результат. Если нет, то ваш dbset отличается от того, с чем работает UserManager ...   -  person B12Toaster    schedule 15.01.2019
comment
@ B12Toaster Count()? Гораздо больше! _userManager.Users.FirstOrDefault(x => x.NormalizedEmail == email) верните желаемого пользователя ... редактирую вопрос, спасибо!   -  person dovid    schedule 15.01.2019
comment
Как вы создали пользователя Data-Seed, поделитесь кодом.   -  person Edward    schedule 15.01.2019
comment
@TaoZhou, я добавил код.   -  person dovid    schedule 15.01.2019


Ответы (2)


Как вы можете видеть в ссылках на исходный код в комментариях, которые я сделал к вашему OP FindByEmailAsync выполняет NormalizeKey, прежде чем он фактически начнет поиск пользователя.

email = NormalizeKey(email);

Это NormalizeKey(email) выполняется UpperInvariantLookupNormalizer, который выполнит следующую строковую операцию с вашим электронным письмом.

return key.Normalize().ToUpperInvariant();

Теперь та часть вашего кода, которая вызывает "странное" поведение, - это отсутствующий вызов нормализации в вашем коде при создании пользователя:

var appUsers = users.Select(x => new ApplicationUser
{
    Email = x.email,
    NormalizedEmail = x.email,
    NormalizedUserName = x.email,
    UserName = x.email,
    EmailConfirmed = true,
    Id = Guid.NewGuid().ToString(),
    SecurityStamp = Guid.NewGuid().ToString()
}).ToArray();

Отсутствие нормализации электронной почты по-прежнему сделает ее обнаруживаемой через таблицу пользователей, поскольку это просто сравнивает NormalizedEmail (который вы не нормализовали при создании пользователя) с ненормализованным электронным письмом, которое вы передаете в качестве аргумента:

_userManager.Users.FirstOrDefault(x => x.NormalizedEmail == email);

... однако userManager.FindByEmailAsync будет сначала нормализуйте его и потом выполните поиск ...

_userManager.FindByEmailAsync(email);

... и поэтому не найти пользователя.

Измените свой код на следующий:

// inject via using Microsoft.AspNetCore.Identity
protected ILookupNormalizer normalizer;

var appUsers = users.Select(x => new ApplicationUser
{
    Email = x.email,
    NormalizedEmail = normalizer.Normalize(x.email),
    NormalizedUserName = normalizer.Normalize(x.email),
    UserName = x.email,
    EmailConfirmed = true,
    Id = Guid.NewGuid().ToString(),
    SecurityStamp = Guid.NewGuid().ToString()
}).ToArray();
person B12Toaster    schedule 15.01.2019

Для NormalizedEmail и NormalizedUserName это должна быть заглавная буква.

Пытаться

var appUsers = users.Select(x => new ApplicationUser
{
    Email = x.email,
    NormalizedEmail = x.email.ToUpper(),
    NormalizedUserName = x.email.ToUpper(),
    UserName = x.email,
    EmailConfirmed = true,
    Id = Guid.NewGuid().ToString(),
    SecurityStamp = Guid.NewGuid().ToString()
}).ToArray();
person Edward    schedule 15.01.2019
comment
Я бы не стал использовать это в производстве, так как он отличается от способа, которым Identity нормализует значения. Простое использование ToUpper проблематично, поскольку это зависит от в текущей культуре, которая может не содержать вариантов в верхнем регистре; Также отсутствует вызов normalize. Таким образом, FindByEmailAsync по-прежнему может не работать с международными доменами. - person B12Toaster; 15.01.2019