使用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

