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

.NET 3.1升级6.0部署AWS报无法解析根提供程序作用域IDbContext异常

问题背景
  • 初始项目基于.NET Core 3.1开发,为托管在AWS平台的Web API项目
  • 本地开发环境升级至.NET 6.0时,仅调整了版本升级必需的配置项,未改动任何业务逻辑代码
  • 本地环境运行验证所有功能正常,部署到AWS后所有请求均触发异常
  • 异常信息为"Cannot resolve scoped service 'IDbContext' from root provider",抛出位置为负责向HttpContext.Items中添加实体的Invoke方法
  • 该Invoke方法入参包含HttpContext与IDbContext,其余移除IDbContext入参的Invoke方法不再抛出异常,但当前方法必须依赖IDbContext查询机构实体并存入HttpContext.Items
  • 同批次完成.NET 6升级的其他API均未出现该问题,可正常运行
相关实现代码

异常对应的InvokeAsync方法

public async Task InvokeAsync(HttpContext httpContext, IDbContext dbContext)
{
    var identifier = GetContextIdentifier(httpContext);

    if (identifier != null)
    {
        string cacheKey = _key + identifier;

        var institution = _cache.Get(cacheKey) as Institution;

        if (institution == null)
        {
            institution = await dbContext.Institutions.Where(x => x.Code == identifier).FirstOrDefaultAsync();

            if (institution != null)
            {
                var cacheEntryOptions = GetCacheEntryOptions();

                _cache.Set(cacheKey, institution, cacheEntryOptions);
            }
        }

        if (institution != null)
        {
            httpContext.Items[institution.GetType().Name] = institution;
        }
    }
}

项目Dockerfile配置

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app

EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["WebAPI.Domain/WebAPI.Domain.csproj", "WebAPI.Domain/"]
COPY ["WebAPI.Application/WebAPI.Application.csproj", "WebAPI.Application/"]
COPY ["WebAPI.Infrastructure/WebAPI.Infrastructure.csproj", "WebAPI.Infrastructure/"]
COPY ["WebAPI.Persistence/WebAPI.Persistence.csproj", "WebAPI.Persistence/"]
COPY ["WebAPI.WebApp/WebAPI.WebApp.csproj", "WebAPI.WebApp/"]
RUN dotnet restore "WebAPI.WebApp/WebAPI.WebApp.csproj"

COPY . .
WORKDIR "/src/WebAPI.WebApp"
RUN dotnet build "WebAPI.WebApp.csproj" -c Release -o /app

FROM build AS publish
RUN dotnet publish "WebAPI.WebApp.csproj" -c Release -o /app

FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "WebAPI.WebApp.dll"]

Startup服务与中间件管道配置

namespace WebAPI.WebApp
{
    public class Startup
    {
        public Startup(IConfiguration configuration, IWebHostEnvironment environment)
        {
            Configuration = configuration;
            Environment = environment;
        }

        public IConfiguration Configuration { get; }
        public IWebHostEnvironment Environment { get; }

        readonly string DevelopmentOrigins = "_developmentOrigins";

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMultitenancy<Tenant, TenantResolver>();

            services.AddDbContext<IDbContext, DbContext>(opt => opt.UseNpgsql(Configuration.GetConnectionString("DatabaseWrite")));
            services.AddDbContext<ITenantDbContext, TenantDbContext>(opt => opt.UseNpgsql(Configuration.GetConnectionString("DatabaseWrite")));

            services.AddCors(options =>
            {
                options.AddDefaultPolicy(builder =>
                {
                    builder.AllowAnyMethod();
                    builder.AllowAnyHeader();
                    builder.WithOrigins("https://*.abc.education", "https://abc.education").SetIsOriginAllowedToAllowWildcardSubdomains();
                });

                options.AddPolicy(DevelopmentOrigins, builder =>
                {
                    builder.AllowAnyMethod();
                    builder.AllowAnyHeader();
                    builder.AllowAnyOrigin();
                });
            });

            services.Configure<AppSettings>((settings) =>
            {
                Configuration.GetSection("AppSettings").Bind(settings);
            });

            services.Configure<AwsOptions>((settings) =>
            {
                Configuration.GetSection("Aws").Bind(settings);
            });

            services.AddSingleton<AuthFilter>();
            services.AddScoped<BasicAuthorizationFilter>();

            services
                .AddControllersWithViews()
                .AddNewtonsoftJson();

            services.AddMvc()
                .AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<IDbContext>());

            services.AddApplication();
            services.AddInfrastructure();
            services.AddControllers();

            services.AddDefaultAWSOptions(Configuration.GetAWSOptions());

            services.AddAWSService<IAmazonS3>();
            services.AddAWSService<IAmazonDynamoDB>();

            services.AddTransient<IDynamoDBContext, DynamoDBContext>(ctx =>
            {
                var client = ctx.GetService<IAmazonDynamoDB>();
                var options = ctx.GetService<IOptions<AwsOptions>>();
                return new DynamoDBContext(client, new DynamoDBContextConfig { TableNamePrefix = options.Value.DynamoDBTableNamePrefix, Conversion = DynamoDBEntryConversion.V2 });
            });

            services.AddScoped<AwsSqsService>();
            services.AddScoped<AwsCloudFrontService>();

            services.AddScoped<FileService>();

            services
               .AddHttpClient<IApplicationService, ApplicationService>()
               .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler() { AllowAutoRedirect = false });
            services
               .AddHttpClient<IInstitutionService, InstitutionService>()
               .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler() { AllowAutoRedirect = false });

            services.AddScoped<IUsernameResolver, UsernameResolver>();
            services.AddScoped<IRoleResolver, RoleResolver>();
            services.AddScoped<IInstitutionResolver, InstitutionResolver>();
            services.AddScoped<IDatabaseResolver, DatabaseResolver>();

            services.AddSingleton(Environment.ContentRootFileProvider);

            services
                .AddHealthChecks()
                .AddNpgSql(Configuration["ConnectionStrings:DatabaseWrite"]);
        }

        private static void HandleWithoutTenant(IApplicationBuilder app)
        {
            app.UseDatabase();
        }
        
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseHealthChecks("/v1/webapi/health");

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/error");
                app.UseHsts();
            }

            app.UseCustomExceptionHandler();

            app.UseRouting();

            if (env.IsEnvironment("Local")) app.UseCors(DevelopmentOrigins);
            else app.UseCors();

            app.UseAuthorization();

            app.UseWhen(context => ShouldUseTenant(context),
                               HandleWithoutTenant);

            app.UseWhen(
                context => !(context.Request.Method.Equals(HttpMethod.Options.Method, StringComparison.OrdinalIgnoreCase)) && !(ShouldUseTenant(context)),
                appBuilder =>
                {
                    appBuilder.UseMultitenancy<Tenant>();
                    appBuilder.UseDatabase();
                    appBuilder.UseInstitutionResolution();
                }
            );

            app.UseHttpsRedirection();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}
故障原因

.NET 6生产环境默认开启严格的依赖注入作用域校验,和.NET Core 3.1的校验规则存在差异:

  • 本地开发环境校验规则宽松,允许部分场景下从根容器解析Scoped生命周期服务做兼容,因此本地运行无异常
  • AWS部署时运行在生产环境,严格禁止从根服务提供者解析Scoped类型服务。机构解析中间件被注册为单例生命周期,中间件实例从根容器创建时,框架尝试从根提供者解析Invoke方法依赖的IDbContext(Scoped生命周期),直接抛出异常。其余中间件未注入Scoped类型参数,因此不会触发该问题。
  • 额外发现贴出的InvokeAsync方法缺失await _next(httpContext)调用,会导致中间件管道截断,请求无法流转到后续逻辑。
修复方案
  1. 检查UseInstitutionResolution扩展方法的实现,不要手动将对应中间件注册为单例,使用标准UseMiddleware<T>()方式注册,让框架按请求作用域创建中间件实例。
  2. 最稳妥的Scoped服务引用方式是从当前请求的作用域容器解析,不要直接通过Invoke方法参数注入,修改后的InvokeAsync实现如下:
public async Task InvokeAsync(HttpContext httpContext)
{
    // 从当前请求作用域解析IDbContext,规避根容器解析异常
    var dbContext = httpContext.RequestServices.GetRequiredService<IDbContext>();
    var identifier = GetContextIdentifier(httpContext);

    if (identifier != null)
    {
        string cacheKey = _key + identifier;

        var institution = _cache.Get(cacheKey) as Institution;

        if (institution == null)
        {
            institution = await dbContext.Institutions.Where(x => x.Code == identifier).FirstOrDefaultAsync();

            if (institution != null)
            {
                var cacheEntryOptions = GetCacheEntryOptions();
                _cache.Set(cacheKey, institution, cacheEntryOptions);
            }
        }

        if (institution != null)
        {
            httpContext.Items[institution.GetType().Name] = institution;
        }
    }

    // 补全管道流转调用
    await _next(httpContext);
}
  1. 重新构建镜像部署到AWS后即可恢复正常。

内容的提问来源于stack exchange,提问作者Otavio Rockenbach

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 00:27:19