ASP.NET Core Identity迁移MongoDB后登录报ToClaim表达式不支持错误
ASP.NET Core Identity迁移MongoDB后登录报错:Expression not supported: i.ToClaim()
问题概述
ASP.NET Core Identity原有功能(注册、JWT登录)正常,通过MongoDB.EntityFrameworkCore迁移到MongoDB后,用户注册正常,但调用signInManager.PasswordSignInAsync时触发异常:
MongoDB.Driver.Linq.ExpressionNotSupportedException: 'Expression not supported: i.ToClaim().'
修改内容
原DbContext配置
builder.Services.AddDbContext<AppDbContext>(ops => ops.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
修改后的DbContext配置
builder.Services.AddDbContext<AppDbContext>(options => options.UseMongoDB(connectionUri, databaseName) );
AppDbContext中的配置
protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity<User>().ToCollection("users"); }
报错场景代码
登录接口中调用PasswordSignInAsync时出错:
var routeGroup = endpoints.MapGroup(""); routeGroup.MapPost("/login", async Task<Results<Ok<AccessTokenResponse>, EmptyHttpResult, ProblemHttpResult>> ([FromBody] LoginRequest login, [FromQuery] bool? useCookies, [FromQuery] bool? useSessionCookies, [FromServices] IServiceProvider sp) => { var signInManager = sp.GetRequiredService<SignInManager<TUser>>(); var useCookieScheme = (useCookies == true) || (useSessionCookies == true); var isPersistent = (useCookies == true) && (useSessionCookies != true); signInManager.AuthenticationScheme = useCookieScheme ? IdentityConstants.ApplicationScheme : IdentityConstants.BearerScheme; // 此处触发报错 var result = await signInManager.PasswordSignInAsync(login.Email, login.Password, isPersistent, lockoutOnFailure: true); if (result.RequiresTwoFactor) { if (!string.IsNullOrEmpty(login.TwoFactorCode)) { result = await signInManager.TwoFactorAuthenticatorSignInAsync(login.TwoFactorCode, isPersistent, rememberClient: isPersistent); } else if (!string.IsNullOrEmpty(login.TwoFactorRecoveryCode)) { result = await signInManager.TwoFactorRecoveryCodeSignInAsync(login.TwoFactorRecoveryCode); } } if (!result.Succeeded) { return TypedResults.Problem(result.ToString(), statusCode: StatusCodes.Status401Unauthorized); } return TypedResults.Empty; });
User类定义
public class User : IdentityUser { public string? DisplayName { get; set; } }
解决方案
方案1:手动实现登录逻辑(临时兼容)
绕开PasswordSignInAsync的自动查询,手动通过UserManager获取用户、验证密码,再执行登录:
var routeGroup = endpoints.MapGroup(""); routeGroup.MapPost("/login", async Task<Results<Ok<AccessTokenResponse>, EmptyHttpResult, ProblemHttpResult>> ([FromBody] LoginRequest login, [FromQuery] bool? useCookies, [FromQuery] bool? useSessionCookies, [FromServices] IServiceProvider sp) => { var signInManager = sp.GetRequiredService<SignInManager<TUser>>(); var userManager = sp.GetRequiredService<UserManager<TUser>>(); var useCookieScheme = (useCookies == true) || (useSessionCookies == true); var isPersistent = (useCookies == true) && (useSessionCookies != true); signInManager.AuthenticationScheme = useCookieScheme ? IdentityConstants.ApplicationScheme : IdentityConstants.BearerScheme; // 手动获取用户 var user = await userManager.FindByEmailAsync(login.Email); if (user == null) { return TypedResults.Problem("用户不存在", statusCode: StatusCodes.Status401Unauthorized); } // 验证密码 var passwordValid = await userManager.CheckPasswordAsync(user, login.Password); if (!passwordValid) { // 处理登录失败逻辑 await userManager.AccessFailedAsync(user); if (await userManager.IsLockedOutAsync(user)) { return TypedResults.Problem("账户已锁定", statusCode: StatusCodes.Status401Unauthorized); } return TypedResults.Problem("密码错误", statusCode: StatusCodes.Status401Unauthorized); } // 重置失败次数 await userManager.ResetAccessFailedCountAsync(user); // 创建ClaimsPrincipal并执行登录 var claimsPrincipal = await signInManager.CreateUserPrincipalAsync(user); await signInManager.SignInAsync(user, isPersistent, signInManager.AuthenticationScheme); return TypedResults.Empty; });
方案2:使用官方MongoDB Identity Provider(长期最优)
微软官方提供的MongoDB.AspNetCore.Identity包专门适配MongoDB,避免EF Core Provider的兼容性问题:
- 安装包:
dotnet add package MongoDB.AspNetCore.Identity - 替换原Identity配置:
builder.Services.AddIdentityMongoDbProvider<User>(options => { // 根据需求配置Identity选项 options.Password.RequireDigit = true; options.Password.RequireLowercase = true; // 其他配置... }, mongoOptions => { mongoOptions.ConnectionString = connectionUri; mongoOptions.DatabaseName = databaseName; }); - 移除原
AppDbContext中关于Identity的所有配置,该包会直接管理MongoDB中的Identity集合。
说明
方案1适合快速修复现有代码,无需更换依赖;方案2是官方推荐的MongoDB Identity集成方式,功能支持更完整,后续不会出现类似LINQ表达式不兼容的问题。
内容的提问来源于stack exchange,提问作者Zoltán
相关产品推荐
相关产品推荐

