将 AddPooledDbContextFactory 与 AspNetCore.Identity 结合使用

问题描述

我有 asp.net 核心 webapi 应用程序,它目前在 Microsoft.AspNetCore.Identity 类的 services.AddDbContext 方法中使用 ConfigureServicesStartup,到目前为止一切正常。

我尝试使用 services.AddPooledDbContextFactory 而不是 services.AddDbContext 来提高性能。但是,当我尝试使用 AddPooledDbContextFactory 时出现错误:

System.InvalidOperationException:无法解析类型的服务 尝试激活时出现“hostapp.Data.DataContext” 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.RoleStore5[hostapp.Models.AppRole,hostapp.Data.DataContext,System.Int32,hostapp.Models.AppUserRole,Microsoft.AspNetCore.Identity.IdentityRoleClaim1[System.Int32]]'

看到这个错误后,我创建了新的 webapi 项目,它使用 AddPooledDbContextFactory 而没有 Microsoft.AspNetCore.Identity 工作正常。所以我的问题是:

services.AddPooledDbContextFactoryMicrosoft.AspNetCore.Identity 结合使用以避免上述错误的正确方法是什么?

ProjectRoot/Startup.cs

using System.Threading.Tasks;
using hostapp.Data;
using hostapp.GraphQL;
using hostapp.Interfaces;
using hostapp.Models;
using hostapp.Services;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace hostapp
{
    public class Startup
    {
        
        // ...
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddScoped<ICookieService,CookieService>();
            
            // EARLIER IMPLEMENTATION WHICH WORKS FINE =>
            // services.AddDbContext<DataContext>(options =>
            // {
            //     options.UseSqlServer(_config.GetConnectionString("DefaultConnection"));
            // });

            // CURRENT IMPLEMENTATION WHICH CAUSES AN ERROR
            services.AddPooledDbContextFactory<DataContext>(options =>
            {
                options.UseSqlServer(_config.GetConnectionString("DefaultConnection"));
            });

            services.AddIdentityCore<AppUser>(opt =>
            {
                opt.Password.RequireNonAlphanumeric = false;
            })
            .AddRoles<AppRole>()
            .AddRoleManager<RoleManager<AppRole>>()
            .AddSignInManager<SignInManager<AppUser>>()
            .AddRoleValidator<RoleValidator<AppRole>>()
            .AddEntityFrameworkStores<DataContext>();

            services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddCookie(options =>
            {
                options.Cookie.Name = "app-cookie";
                // options.Cookie.Expiration = TimeSpan.FromMinutes(30);
                options.Events.OnRedirectToLogin = context =>
                {
                    context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                    return Task.CompletedTask;
                };
            });

            services.AddAuthorization(opt =>
            {
                opt.AddPolicy("RequireAdminRole",policy => policy.RequireRole("Admin"));
            });

            services
                .AddGraphQLServer()
                .AddAuthorization()
                .AddQueryType<Query>();

            services.AddControllers();
        }

        public void Configure(IApplicationBuilder app,IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseCors(options => options
                .AllowAnyHeader()
                .AllowAnyMethod()
                .WithOrigins("https://localhost:4200")
                .SetIsOriginAllowed(origin => true)
                .AllowCredentials());

            app.UseCookiePolicy(new CookiePolicyOptions
            {
                MinimumSameSitePolicy = SameSiteMode.Strict,// HttpOnly = Microsoft.AspNetCore.CookiePolicy.HttpOnlyPolicy.Always,// Secure = CookieSecurePolicy.Always
            });

            app.UseAuthentication();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
                endpoints.MapGraphQL();
            });
        }
    }
}

ProjectRoot/Data/DataContext.cs

using hostapp.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

namespace hostapp.Data
{
    public class DataContext : IdentityDbContext<AppUser,AppRole,int,IdentityUserClaim<int>,AppUserRole,IdentityUserLogin<int>,IdentityRoleClaim<int>,IdentityUserToken<int>>
    {
        public DataContext(DbContextOptions<DataContext> options) : base(options)
        {
        }

        public DbSet<AppUserClaim> Claims { get; set; }
        public DbSet<AppUserClaimOffender> Offenders { get; set; }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);

            builder.Entity<AppUser>()
                .HasMany(ur => ur.AppUserRoles)
                .WithOne(u => u.AppUser)
                .HasForeignKey(ur => ur.UserId)
                .IsRequired();

            builder.Entity<AppRole>()
                .HasMany(ur => ur.AppUserRoles)
                .WithOne(u => u.AppRole)
                .HasForeignKey(ur => ur.RoleId)
                .IsRequired();
        }
    }
}

ProjectRoot/app.csproj

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="2.2.0"/>
    <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.0" NoWarn="NU1605"/>
    <PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="5.0.0" NoWarn="NU1605"/>
    <PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3"/>
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.0"/>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.0"/>
    <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="5.0.0"/>
    <PackageReference Include="HotChocolate.AspNetCore" Version="11.1.0"/>
    <PackageReference Include="HotChocolate.AspNetCore.Authorization" Version="11.1.0"/>
    <PackageReference Include="HotChocolate.Data.EntityFramework" Version="11.1.0"/>
    <PackageReference Include="Microsoft.AspNetCore.Cors" Version="2.2.0"/>
  </ItemGroup>
</Project>

解决方法

使用services的正确方法是什么。AddPooledDbContextFactory 使用 Microsoft.AspNetCore.Identity 以避免上述错误?

在 ConfigureServices 中,尝试使用 AddScoped() 方法注册 DBContext 并使用提供程序从服务中获取工厂。然后,将实例返回给提供者。代码如下:

        services.AddPooledDbContextFactory<DataContext>(options =>
        {
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
            options.EnableSensitiveDataLogging();
        },poolSize:32);

        services.AddScoped<DataContext>(p => p.GetRequiredService<IDbContextFactory<DataContext>>().CreateDbContext());

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...