如何基于IDataCheck服务动态创建ASP.NET Core健康检查
问题背景
我有大量用于执行数据校验的服务,所有服务均实现了 IDataCheck 接口,该接口仅定义了一个 RunCheckAsync 方法。
此前在命令行应用中,我通过如下代码执行所有校验逻辑:
IEnumerable<IDataCheck>? services = _serviceProvider.GetServices<IDataCheck>(); foreach (IDataCheck? s in services) { DataCheckResult result = await s.RunCheckAsync(); // 处理校验结果,比如给出自动修复建议等 }
现在需要把这些数据校验逻辑接入ASP.NET Core健康检查体系,同时支持HealthCheck UI展示,需要满足两个核心要求:
- 不需要为每个数据校验服务手动编写对应的健康检查实现,避免破坏
IDataCheck的接口抽象价值 - 不能把所有校验逻辑合并到单个健康检查中,避免UI端把所有校验结果堆在无格式的纯文本字段里,需要每个校验项在UI中独立、格式化展示
实现方案
你可以通过动态健康检查注册的方式同时满足两个诉求,无需为每个IDataCheck实现手动编写健康检查类,也能保证每个校验项在HealthCheck UI中独立展示。
1. 编写通用适配类
先实现一个通用的健康检查包装类,负责把任意IDataCheck实例的执行结果映射为健康检查返回格式:
public class DataCheckHealthCheck : IHealthCheck { private readonly IDataCheck _dataCheck; private readonly string _checkDisplayName; // 构造函数传入具体的校验服务实例和展示名称 public DataCheckHealthCheck(IDataCheck dataCheck, string checkDisplayName) { _dataCheck = dataCheck; _checkDisplayName = checkDisplayName; } public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) { try { DataCheckResult checkResult = await _dataCheck.RunCheckAsync(cancellationToken); // 按你自己的DataCheckResult结构映射健康状态 if (checkResult.Passed) { return HealthCheckResult.Healthy($"{_checkDisplayName} 校验通过", checkResult.Metadata); } return checkResult.IsWarning ? HealthCheckResult.Degraded($"{_checkDisplayName} 存在警告:{checkResult.Message}", data: checkResult.Metadata) : HealthCheckResult.Unhealthy($"{_checkDisplayName} 校验失败:{checkResult.Message}", data: checkResult.Metadata); } catch (Exception ex) { return HealthCheckResult.Unhealthy($"{_checkDisplayName} 执行异常", ex); } } }
2. 实现自动注册扩展
编写扩展方法,启动时自动扫描所有注册到DI的IDataCheck实例,逐个注册为独立的健康检查条目,这里用启动过滤器避免提前构建服务提供者导致的生命周期异常:
public static class DataCheckHealthCheckExtensions { public static IHealthChecksBuilder AddAllDataValidationChecks(this IHealthChecksBuilder builder) { builder.Services.AddSingleton<IStartupFilter>(sp => new DataCheckRegistrationStartupFilter()); return builder; } private class DataCheckRegistrationStartupFilter : IStartupFilter { public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next) { return app => { var healthChecksBuilder = app.ApplicationServices.GetRequiredService<IHealthChecksBuilder>(); var allDataChecks = app.ApplicationServices.GetServices<IDataCheck>(); foreach (var check in allDataChecks) { // 用实现类名生成UI展示名,你可以替换成自己的命名规则 string displayName = check.GetType().Name .Replace("DataCheck", string.Empty) .Replace("Check", string.Empty); // 每个校验项注册为独立健康检查,打datacheck标签方便分组 healthChecksBuilder.AddCheck( name: $"数据校验-{displayName}", instance: new DataCheckHealthCheck(check, displayName), failureStatus: HealthStatus.Unhealthy, tags: new[] { "datacheck" } ); } next(app); }; } } }
3. 配置服务和中间件
在Program.cs中按顺序注册服务即可:
// 按原有方式注册所有IDataCheck实现,生命周期保持你之前的设置即可 builder.Services.AddTransient<IDataCheck, UserInfoDataCheck>(); builder.Services.AddTransient<IDataCheck, OrderConfigDataCheck>(); // 后续新增IDataCheck实现只需要在这里注册,会被自动纳入健康检查 // 注册健康检查,调用扩展自动注册所有数据校验项 builder.Services.AddHealthChecks() .AddAllDataValidationChecks(); // 配置中间件,开启UI响应格式支持 app.UseHealthChecks("/health", new HealthCheckOptions { ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse }); // 配置HealthCheck UI app.UseHealthChecksUI(opt => { opt.UIPath = "/health-dashboard"; // 其余UI配置按实际需求调整即可 });
注意:如果你的校验逻辑需要依赖Scoped生命周期的服务(比如数据库上下文),只需要把
DataCheckHealthCheck的执行逻辑改为工厂模式,在健康检查执行时手动创建作用域解析对应IDataCheck实例即可,不需要修改核心适配逻辑。
方案优势
- 零额外重复代码:新增
IDataCheck实现时只需要按原有逻辑注册到DI,不需要编写任何健康检查相关代码,完全保留接口抽象的价值 - 独立条目展示:每个数据校验项对应独立的健康检查节点,HealthCheck UI会单独展示每个校验项的状态、名称、详情,不会出现所有结果堆在纯文本块的问题
- 灵活扩展:你可以根据需要给不同校验项配置独立的标签、失败状态、缓存时长,也可以在适配层自由映射校验结果到健康检查的结构化数据字段,支持UI展示自动修复建议等自定义内容
内容的提问来源于stack exchange,提问作者edelwater
相关产品推荐
相关产品推荐

