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

使用AddPooledDbContextFactory整合AspNetCore.Identity与DataProtection遇错

问题描述

使用Duende IdentityServer + AspNetCore.Identity搭建API,通过DataProtection加密数据库用户数据;同时需用Hot Chocolate(GraphQL)暴露用户列表,该框架要求使用池化DbContext避免并行查询报错。但切换为AddPooledDbContextFactory后,AspNetCore.Identity与DataProtection无法协同工作,之前用AddDbContext<>时一切正常。

相关代码:

var redis = ConnectionMultiplexer.Connect(configurationOptions);
services.AddSingleton(redis);
services.AddDataProtection().SetApplicationName("app").PersistKeysToStackExchangeRedis(redis, "DataProtectionKeys");
services.AddScoped<ILookupProtectorKeyRing, KeyRing>();
services.AddScoped<ILookupProtector, LookupProtector>();
services.AddScoped<IPersonalDataProtector, PersonalDataProtector>();

// 其他代码
    
services.AddPooledDbContextFactory<IdentityContext>((ctx) => ctx.UseNpgsql(dataOptions.ConnectionString));
services.AddScoped<IdentityContext>(p => p.GetRequiredService<IDbContextFactory<IdentityContext>>().CreateDbContext());
services.AddIdentity<User, IdentityRole>(options => {
    options.Stores.ProtectPersonalData = true;
    options.Stores.MaxLengthForKeys = 128;
})
    .AddEntityFrameworkStores<IdentityContext>()
    .AddDefaultTokenProviders();

报错信息:

Cannot resolve scoped service 'Microsoft.AspNetCore.Identity.IPersonalDataProtector' from root provider. 
解决思路
  • 问题本质:AddPooledDbContextFactory创建DbContext时默认使用根服务提供者,而你注册的IPersonalDataProtector是Scoped生命周期,根提供者没有Scope上下文,无法解析Scoped服务。

  • 调整DbContext注入策略:同时注册Scoped的DbContext供Identity使用,保留池化工厂供GraphQL调用:
    移除原有的AddScoped<IdentityContext>注册,替换为以下代码:

    // 为Identity注册Scoped生命周期的DbContext
    services.AddDbContext<IdentityContext>((sp, options) => 
        options.UseNpgsql(dataOptions.ConnectionString)
               .UseInternalServiceProvider(sp), 
        contextLifetime: ServiceLifetime.Scoped, 
        optionsLifetime: ServiceLifetime.Singleton);
    
    // 为GraphQL注册池化DbContext工厂
    services.AddPooledDbContextFactory<IdentityContext>((sp, options) => 
        options.UseNpgsql(dataOptions.ConnectionString)
               .UseInternalServiceProvider(sp));
    

    这样Identity会使用Scoped的DbContext,能正常获取Scoped的IPersonalDataProtector;GraphQL则通过IDbContextFactory<IdentityContext>获取池化实例,满足并行查询需求。

  • 确认DataProtection服务生命周期:IPersonalDataProtector本身就是Scoped服务(依赖请求级加密上下文),必须保证DbContext在Scoped范围内创建,才能完成注入。

  • 验证Identity配置:确保User类正确实现个人数据加密逻辑,比如给需要加密的属性添加[ProtectedPersonalData]特性,且options.Stores.ProtectPersonalData = true配置生效。

内容的提问来源于stack exchange,提问作者Dylan Snel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 17:46:27