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

在.NET Core健康检查RunCheckAsync中调用Scoped注入类方法的问题

解决方案

要解决健康检查RunCheckAsync作用域内调用Scoped类方法的问题,核心是在健康检查的作用域中获取请求上下文的元数据,并控制Scoped服务与DbContext的初始化顺序,具体步骤如下:

1. 注册HttpContext访问器

让健康检查能访问当前请求的HttpContext,先注册IHttpContextAccessor:

services.AddHttpContextAccessor();

2. 编写中间件传递客户标识符

在请求管道中添加中间件,从HttpRequest提取客户标识符并存入HttpContext.Items(确保中间件注册在健康检查端点之前):

public class CustomerIdentifierMiddleware
{
    private readonly RequestDelegate _next;

    public CustomerIdentifierMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // 示例:从请求Header提取客户标识符,可根据实际场景调整(如Query、Claims等)
        var customerId = context.Request.Headers["X-Customer-Id"].FirstOrDefault();
        if (!string.IsNullOrEmpty(customerId))
        {
            context.Items["CustomerId"] = customerId;
        }

        await _next(context);
    }
}

// 注册中间件
app.UseMiddleware<CustomerIdentifierMiddleware>();

3. 自定义健康检查替代内置的AddDbContextCheck

放弃框架内置的AddDbContextCheck,自定义健康检查类,在其中控制Scoped服务的调用与DbContext的初始化:

public class MyDbContextHealthCheck : IHealthCheck
{
    private readonly IServiceScopeFactory _scopeFactory;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public MyDbContextHealthCheck(IServiceScopeFactory scopeFactory, IHttpContextAccessor httpContextAccessor)
    {
        _scopeFactory = scopeFactory;
        _httpContextAccessor = httpContextAccessor;
    }

    public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        // 1. 从请求上下文获取客户标识符
        var httpContext = _httpContextAccessor.HttpContext;
        if (httpContext == null || !httpContext.Items.TryGetValue("CustomerId", out var customerIdObj) || 
            customerIdObj is not string customerId)
        {
            return HealthCheckResult.Unhealthy("无法获取客户标识符");
        }

        // 2. 创建健康检查专属作用域(对应RunCheckAsync的作用域)
        using var scope = _scopeFactory.CreateScope();
        var scopedService = scope.ServiceProvider.GetRequiredService<IMyScopedService>();
        var dbContext = scope.ServiceProvider.GetRequiredService<MyDbCtxContext>();

        // 3. 调用Scoped服务的SetIdentifier方法
        scopedService.SetIdentifier(customerId);

        // 4. 执行数据库健康检查逻辑
        try
        {
            var canConnect = await dbContext.Database.CanConnectAsync(cancellationToken);
            return canConnect ? HealthCheckResult.Healthy("数据库连接正常") : HealthCheckResult.Unhealthy("数据库连接失败");
        }
        catch (Exception ex)
        {
            return HealthCheckResult.Unhealthy("数据库检查异常", ex);
        }
    }
}

4. 配置DbContext依赖Scoped服务

确保MyDbCtxContext在初始化时使用Scoped服务的标识符生成动态连接字符串:

public class MyDbCtxContext : DbContext
{
    private readonly IMyScopedService _scopedService;

    public MyDbCtxContext(DbContextOptions<MyDbCtxContext> options, IMyScopedService scopedService) : base(options)
    {
        _scopedService = scopedService;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            // 根据Scoped服务的标识符获取对应连接字符串
            var connectionString = GetConnectionString(_scopedService.GetIdentifier());
            optionsBuilder.UseSqlServer(connectionString);
        }
    }

    // 实现根据客户ID获取动态连接字符串的逻辑
    private string GetConnectionString(string customerId)
    {
        return $"Server=your-server;Database=Customer_{customerId};Trusted_Connection=True;";
    }
}

5. 注册自定义健康检查

将自定义健康检查注册到服务中:

services.AddHealthChecks()
        .AddCheck<MyDbContextHealthCheck>(
            "dbContextChecks", 
            tags: HealthCheckTagsConstants.ReadinessTags);

内容的提问来源于stack exchange,提问作者Andy Maddio

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 22:32:04