You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的兼容性问题:

  1. 安装包:
    dotnet add package MongoDB.AspNetCore.Identity
    
  2. 替换原Identity配置:
    builder.Services.AddIdentityMongoDbProvider<User>(options =>
    {
        // 根据需求配置Identity选项
        options.Password.RequireDigit = true;
        options.Password.RequireLowercase = true;
        // 其他配置...
    }, mongoOptions =>
    {
        mongoOptions.ConnectionString = connectionUri;
        mongoOptions.DatabaseName = databaseName;
    });
    
  3. 移除原AppDbContext中关于Identity的所有配置,该包会直接管理MongoDB中的Identity集合。

说明

方案1适合快速修复现有代码,无需更换依赖;方案2是官方推荐的MongoDB Identity集成方式,功能支持更完整,后续不会出现类似LINQ表达式不兼容的问题。

内容的提问来源于stack exchange,提问作者Zoltán

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 02:22:14