.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

