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

.NET Core 6依赖注入无法解析BrokenConfigClass问题求助

问题描述

我有一个.NET Core 6 API项目,此前已成功将appsettings.json中的配置加载到类中,并通过依赖注入注入到服务里。现在新增了一个配置,但调用健康检查服务时抛出以下异常:

Unable to resolve service for type 'BrokenConfigClass' while attempting to activate 'HealthCheckUsingBrokenConfig'.

堆栈跟踪:

at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.ConstructorMatcher.CreateInstance(IServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, Object[] parameters)
   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetServiceOrCreateInstance[T](IServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.HealthChecksBuilderAddCheckExtensions.<>c__3`1.<AddCheck>b__3_0(IServiceProvider s)
   at Microsoft.Extensions.Diagnostics.HealthChecks.DefaultHealthCheckService.RunCheckAsync(HealthCheckRegistration registration, CancellationToken cancellationToken)
   at Microsoft.Extensions.Diagnostics.HealthChecks.DefaultHealthCheckService.CheckHealthAsync(Func`2 predicate, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckMiddleware.InvokeAsync(HttpContext httpContext)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Serilog.AspNetCore.RequestLoggingMiddleware.Invoke(HttpContext httpContext)
[03:31:13 ERR] Connection id "0HMNND6U6RR42", Request id "0HMNND6U6RR42:00000001": An unhandled exception was thrown by the application.

其他配置均正常工作,配置方式与新配置完全一致。相关代码如下:

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    ...

    services.AddConfig(configuration);

    ...

    services
        .AddHealthChecks()
        .AddCheck<HealthCheckUsingBrokenConfig>(nameof(HealthCheckUsingBrokenConfig));

    ...
}

配置扩展方法

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddConfig(this IServiceCollection services, IConfiguration configuration)
    {
        services.Configure<WorkingConfig1>(configuration.GetSection("Level1:SubLevel1:WorkingConfig1"));
        services.Configure<WorkingConfig2>(configuration.GetSection("Level1:SubLevel2:WorkingConfig2"));
        services.Configure<WorkingConfig3>(configuration.GetSection("Level1:SubLevel3:WorkingConfig3"));
        services.Configure<BrokenConfigClass>(configuration.GetSection("HealthChecks:BrokenConfigClass"));

        return services;
    }
}

appsettings.json

{
    "Level1": {
        "SubLevel1": {
            "WorkingConfig1": {
                "Property1": "Value",
                "Property2": "Value",
                "Property3": "Value"
            }
        },
        "SubLevel2": {
            "WorkingConfig2": {
                "Property1": "Value",
                "Property2": "Value",
                "Property3": "Value"
            }
        },

        "SubLevel3": {
            "WorkingConfig3": {
                "Property1": "Value",
                "Property2": "Value",
                "Property3": "Value"
            }
        }
    },
    "HealthChecks": {
        "BrokenConfigClass": {
            "DelayInMinutes": 60
        }
    }
}

BrokenConfigClass

public class BrokenConfigClass
{
    public int DelayInMinutes { get; set; }
}

HealthCheckUsingBrokenConfig

public class HealthCheckUsingBrokenConfig : IHealthCheck
{
    private readonly BrokenConfigClass healthCheckConfig;
    private readonly DatabaseContext databaseContext;

    public HealthCheckUsingBrokenConfig(DatabaseContext databaseContext, BrokenConfigClass healthCheckConfig)
    {
        this.databaseContext = databaseContext;
        this.healthCheckConfig = healthCheckConfig;
    }

    public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        // Some logic
    }
}

已确认配置路径正确,直接通过configuration.GetSection("HealthChecks:BrokenConfigClass").GetValue<int>("DelayInMinutes")能获取到正确值60,但依赖注入始终失败。


解决方案

问题根源

使用services.Configure<T>()注册的配置,并不会直接将T类型本身注入到DI容器中,而是注册了一个IOptions<T>的实例。健康检查类直接请求BrokenConfigClass类型,DI容器找不到对应的服务注册,因此抛出解析失败的异常。

其他配置能正常工作,大概率是因为那些服务是通过IOptions<T>来接收配置的,而非直接请求配置类本身。

修复步骤

方案1:修改健康检查类,通过IOptions<T>接收配置

这是.NET Core配置系统的标准用法,推荐采用:

public class HealthCheckUsingBrokenConfig : IHealthCheck
{
    private readonly BrokenConfigClass healthCheckConfig;
    private readonly DatabaseContext databaseContext;

    // 改为注入IOptions<BrokenConfigClass>
    public HealthCheckUsingBrokenConfig(DatabaseContext databaseContext, IOptions<BrokenConfigClass> healthCheckConfigOptions)
    {
        this.databaseContext = databaseContext;
        // 通过Value属性获取配置实例
        this.healthCheckConfig = healthCheckConfigOptions.Value;
    }

    public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        // 使用healthCheckConfig.DelayInMinutes进行逻辑处理
        return Task.FromResult(HealthCheckResult.Healthy());
    }
}

方案2:将配置类直接注册为DI服务(不推荐)

如果一定要直接注入BrokenConfigClass,可以在配置扩展方法中额外添加注册:

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddConfig(this IServiceCollection services, IConfiguration configuration)
    {
        services.Configure<WorkingConfig1>(configuration.GetSection("Level1:SubLevel1:WorkingConfig1"));
        services.Configure<WorkingConfig2>(configuration.GetSection("Level1:SubLevel2:WorkingConfig2"));
        services.Configure<WorkingConfig3>(configuration.GetSection("Level1:SubLevel3:WorkingConfig3"));
        
        var brokenConfig = configuration.GetSection("HealthChecks:BrokenConfigClass").Get<BrokenConfigClass>();
        services.AddSingleton(brokenConfig);

        return services;
    }
}

注意:这种方式会将配置实例注册为单例,无法自动获取配置变更(如果启用了配置重载),而IOptions<T>支持配置热重载,因此优先推荐方案1。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 04:50:25