.NET依赖注入:非IOptions对象如何在启动时执行验证
问题
我希望在应用启动时加载JSON文件和JSON Schema文件,并用加载的Schema验证JSON内容。目前只发现AddOptions<>支持在启动时执行验证,想问下能不能通过TryAddSingleton()或者其他方法实现类似功能?
当前使用TryAddSingleton()的实现代码
private static IServiceCollection AddConfig<TConfig>( this IServiceCollection services, string jsonSchemaPath) where TConfig : class, IConfig { services.TryAddSingleton((sp) => { var configService = sp.GetRequiredService<IConfigService>(); var config = configService.LoadAsync<TConfig>( jsonSchemaPath) .GetAwaiter() .GetResult(); if (config is null) { throw new ConfigMissingException(nameof(TConfig)); } bool valid = config.Validate(); if (!valid) { throw new ConfigInvalidException( nameof(TConfig), config .GetValidationErrors() .ToList()); } return config; }); return services; }
更新后的AddOptions<>实现代码
services .AddOptions<TConfig>() .Configure<IConfigService>((config, configService) => { var c = configService.LoadAsync<TConfig>( jsonSchemaPath) .GetAwaiter() .GetResult(); if (c is null) { throw new ConfigMissingException(nameof(TConfig)); } // 在这里填充配置 }) .Validate((c) => { bool valid = c.Validate(); if (!valid) { throw new ConfigInvalidException( nameof(TConfig), c .GetValidationErrors() .ToList()); } return valid; }) .ValidateOnStart();
实现方案
当然可以通过TryAddSingleton()实现启动时的配置加载与验证,你的第一版代码其实已经达成了核心目标——在单例服务的工厂委托中同步加载配置、执行验证,一旦验证失败就抛出异常,这样应用启动时会因为服务注册失败而直接终止,和AddOptions<>的ValidateOnStart()效果完全一致。
需要注意几个细节:
- 避免异步阻塞风险:你当前用
GetAwaiter().GetResult()实现同步等待异步方法,但如果LoadAsync内部依赖了需要同步上下文的资源,可能引发死锁。如果IConfigService提供同步加载方法,优先使用同步方法;如果只能用异步,建议在启动时通过异步初始化逻辑处理,比如利用Host.CreateDefaultBuilder的异步配置扩展,或者使用异步服务提供者来规避死锁问题。 - 错误处理的清晰度:自定义异常的方式没问题,但要确保这些异常能被应用启动时的错误捕获逻辑处理,让启动失败时输出明确的错误信息,方便排查问题。
除了TryAddSingleton(),还有两种可行的实现方式:
- 使用
IHostedService:编写一个继承IHostedService的配置初始化服务,在StartAsync方法中加载并验证配置,验证失败直接抛出异常。这种方式会在应用启动阶段触发逻辑,但需要手动将配置实例注入容器,不如单例注册直接。 - 手动触发验证:在
ConfigureServices方法结束后,提前构建服务提供者并解析配置实例,比如调用services.BuildServiceProvider().GetRequiredService<TConfig>(),强制触发加载和验证。但这种方式会提前构建服务容器,可能引发部分服务注册的生命周期问题,需谨慎使用。
对比来看,你的TryAddSingleton()方案已经足够简洁,只要解决异步阻塞的潜在问题,完全可以替代AddOptions<>的方案。如果更倾向于遵循ASP.NET Core的原生配置规范,AddOptions<>的方案会更合适,因为它集成了框架的配置绑定、验证体系,还支持配置重载等高级特性。
内容的提问来源于stack exchange,提问作者Mivaweb
相关产品推荐
相关产品推荐

