使用EF Core InMemory数据库提供程序时出现登录失败的原因
问题场景
自定义AuthUser身份用户类,在EF Core搭配SQL数据库运行时功能完全正常,切换为EF Core InMemory内存数据库提供程序后出现两个异常:
- 调用
UserManager<AuthUser>.FindByNameAsync()方法无法查询到已存在的用户,只能改用UserManager<AuthUser>.Users.FirstOrDefault(u => u.Email == "")的方式查询 - 即便通过上述方式查询到目标用户,调用
SignInManager.PasswordSignInAsync()方法执行登录时,即使输入密码正确也会返回登录失败
排查过程中发现读取AuthUser实体时,NormalizedUserName和NormalizedEmail字段没有按预期转为大写格式,判断是该问题导致异常。先后尝试以下修复方案均未生效:
- 重写实体的规范化属性,代码如下:
public override string NormalizedUserName { get => base.UserName.ToUpper(); set => base.NormalizedUserName = value; }
- 创建用户时显式设置这两个规范化字段的值
- 数据写入后手动更新这两个字段的值
所有修改在每次通过UserManager读取用户时都会被还原,无法持久化。
关联代码
AuthUser 实体定义
public class AuthUser : Microsoft.AspNetCore.Identity.IdentityUser<string> { public DateTime? LastLogin { get; set; } public string? PasswordResetKey { get; set; } }
实体Fluent API配置
builder .ToTable("AspNetUsers"); builder .HasKey((e) => e.Id); builder .Property((e) => e.Id) .ValueGeneratedOnAdd();
问题原因
SQL Server默认使用不区分大小写的排序规则,哪怕NormalizedUserName、NormalizedEmail字段没有正确存为大写格式,查询时的字符串匹配也能命中,所以不会暴露问题;但InMemory数据库的字符串比较是严格大小写敏感的,Identity内置的用户查询、密码校验前的用户匹配逻辑,都是基于大写规范化后的这两个字段做精确匹配,一旦字段值大小写不符合预期就会匹配失败。
之前尝试的修复方案不生效的核心原因:
- 重写属性的写法干扰了EF Core的属性存取逻辑,EF Core读取实体时会直接给属性的支撑字段赋值,不会触发自定义的get/set逻辑,无法自动转换大小写
- 直接操作DbContext插入/修改用户数据,绕过了UserManager的内置处理流程,UserManager在跟踪实体时会自动调用内置的规范化器重新处理这两个字段,覆盖手动修改的值
修复方案
- 删掉重写的
NormalizedUserName属性代码,不要通过实体重写的方式实现规范化逻辑,避免干扰EF Core的正常属性存取 - 所有用户创建、更新操作统一通过
UserManager的CreateAsync、UpdateAsync方法执行,这些方法内部会自动调用ILookupNormalizer把NormalizedUserName、NormalizedEmail转为大写,同时自动完成密码哈希、安全标记更新等必要逻辑,不要直接操作DbContext增改用户数据 - 检查服务配置代码,确保调用了
AddIdentity/AddDefaultIdentity/AddIdentityCore方法注册Identity服务,这些方法默认会注册实现大写转换逻辑的ILookupNormalizer,如果之前手动替换过该服务实现,要确保规范化逻辑正确。
内容的提问来源于stack exchange,提问作者Bassie
相关产品推荐
相关产品推荐

