如何在DI中注册单个Options实例供两个类共用?
解决继承Options类的DI实例一致性问题
问题核心
当库中同时需要注入IOptions<BaseOptions>和IOptions<TOptions>(其中TOptions : BaseOptions)时,默认的DI注册会生成两个独立实例,导致配置状态不一致。我们需要让这两个IOptions返回同一个对象实例。
可行实现方案
在库的泛型注册方法中,通过自定义IOptions<BaseOptions>的注册逻辑,让它指向IOptions<TOptions>的实例,确保两者共享同一个配置对象。
具体代码实现
public static class MyLibraryServiceCollectionExtensions { public static IServiceCollection AddMyLibrary<TOptions>(this IServiceCollection services) where TOptions : BaseOptions, new() { // 1. 注册TOptions的配置系统,允许用户后续配置 services.AddOptions<TOptions>() .Configure(options => { // 设置BaseOptions的默认值 options.Mode = "DefaultMode"; }); // 2. 注册IOptions<BaseOptions>,让它返回TOptions的实例 services.AddSingleton<IOptions<BaseOptions>>(serviceProvider => { // 从DI中获取已配置的IOptions<TOptions> var customOptions = serviceProvider.GetRequiredService<IOptions<TOptions>>(); // 用OptionsWrapper包装TOptions实例,转换为IOptions<BaseOptions> return new OptionsWrapper<BaseOptions>(customOptions.Value); }); // 3. 注册库内的泛型服务 services.AddTransient(typeof(SomeServiceInMyLib<>)); return services; } }
验证效果
用户使用库时,只需注册自定义选项并配置:
services.AddMyLibrary<CustomOptions>() .Configure<CustomOptions>(options => { options.Mode = "Production"; options.OperationsCount = 50; });
此时:
- 注入
IOptions<BaseOptions>时,Value.Mode为"Production",与IOptions<CustomOptions>的Value是同一个对象。 - 注入
IOptions<CustomOptions>时,既能访问Mode也能访问自定义的OperationsCount,配置状态完全一致。
方案原理
OptionsWrapper<T>是.NET Options框架提供的轻量级IOptions<T>实现,直接包装一个已有的实例。- 通过让
IOptions<BaseOptions>的解析逻辑复用IOptions<TOptions>的实例,避免了重复创建配置对象,保证了状态一致性。 - 所有针对
TOptions的配置(包括用户的自定义配置)都会直接作用于这个共享实例,BaseOptions的注入者也能获取到最新配置。
内容的提问来源于stack exchange,提问作者Dalival
相关产品推荐
相关产品推荐

