选项框架特性
- 支持单例模式读取配置
- 支持快照
- 支持配置变更通知
- 支持运行时动态修改选项值
设计原则
- 接口分离原则(ISP),我们的类不应该依赖它不使用的配置
- 关注点分离(SoC),不同组件、服务、类之间的配置不应相互依赖或耦合
建议
- 为我们的服务设计
XXXOptions
- 使用
IOptions<XXXOptions>、IOptionsSnapshot<XXXOptions>、IOptionsMonitor<XXXOptions>作为服务构造函数的参数
代码实现
创建名为OptionsDemo的ASP.NET Core项目,类型为API
创建测试服务与测试服务对应的选项
创建OrderService.cs为了方便测试,这里将IOrderService、OrderService、OrderServiceOption都放在OrderService.cs文件中,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| using Microsoft.Extensions.Options;
namespace OptionsDemo.Services { public interface IOrderService { int ShowMaxOrderCount(); }
public class OrderService:IOrderService {
private readonly IOptions<OrderServiceOptions> _options; public OrderService(IOptions<OrderServiceOptions> options) { _options = options; } public int ShowMaxOrderCount() { return _options.Value.MaxOrderCount; } }
public class OrderServiceOptions { public int MaxOrderCount { get; set; } = 100; } }
|
修改appsettings.json
将项目中的appsettings.json的内容修改如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| { "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*", "OrderService": { "MaxOrderCount": 400 } }
|
注册服务和选项
在Startup.ConfigureServices中添加以下代码:
1 2
| services.Configure<OrderServiceOptions>(Configuration.GetSection("OrderService")); services.AddScoped<IOrderService, OrderService>();
|
获取服务进行测试
修改WeatherForecastController.Get方法,具体代码如下:
1 2 3 4 5 6
| [HttpGet] public int Get([FromServices]IOrderService orderService) { Console.WriteLine($"orderService.ShowMaxOrderCount:{orderService.ShowMaxOrderCount()}"); return 1; }
|
运行项目,可以访问/WeatherForecast,可以看到控制台打印出以下信息:
1
| orderService.ShowMaxOrderCount:400
|
这里如果在项目运行时修改appsettings.json里配置项的值,重新访问/WeatherForecast地址,会发现控制台打印出来的值不会变,还依旧是400,这里就需要使用到下面的热更新。
选项框架热更新
关键类型
- 单例服务(Singleton)使用
IOptionsMonitor<out TOptions>
- 范围作用域类型(Scope)使用
IOptionsSnapshot<out TOptions>
代码示例
IOptionsSnapshot
上个示例对OrderService注册的是Scope服务,所以这里先测试Scope对应的IOptionsSnapshot,将OrderService构造函数获取服务的类型修改为IOptionsSnapshot<OrderServiceOptions>,最终修改后OrderService类的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12
| public class OrderService:IOrderService { private readonly IOptionsSnapshot<OrderServiceOptions> _options; public OrderService(IOptionsSnapshot<OrderServiceOptions> options) { _options = options; } public int ShowMaxOrderCount() { return _options.Value.MaxOrderCount; } }
|
运行代码,访问/WeatherForecast,发现现在打印出来的是appsettings.json现有的值,对该配置项进行修改,保存之后重新访问/WeatherForecast,可以发现获取到的是新的值
IOptionsMonitor
将OrderService构造函数获取服务的类型修改为IOptionsMonitor<OrderServiceOptions>,最终修改后OrderService类的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12
| public class OrderService:IOrderService { private readonly IOptionsMonitor<OrderServiceOptions> _options; public OrderService(IOptionsMonitor<OrderServiceOptions> options) { _options = options; } public int ShowMaxOrderCount() { return _options.CurrentValue.MaxOrderCount; } }
|
在Startup.ConfigureServices方法中将OrderService注册为单例模式,代码如下:
1
| services.AddSingleton<IOrderService, OrderService>();
|
运行代码,访问/WeatherForecast,发现现在打印出来的是appsettings.json现有的值,对该配置项进行修改,保存之后重新访问/WeatherForecast,可以发现获取到的是新的值
IOptionsMonitor监听配置变动
通过IOptionsMonitor对象的OnChange方法来注册配置变动操作,只需要在获取对象后注册相应操作即可,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class OrderService:IOrderService { private readonly IOptionsMonitor<OrderServiceOptions> _options; public OrderService(IOptionsMonitor<OrderServiceOptions> options) { _options = options; this._options.OnChange(changedOptions => { Console.WriteLine($"配置发生了变化,新值为:{changedOptions.MaxOrderCount}"); }); } public int ShowMaxOrderCount() { return _options.CurrentValue.MaxOrderCount; } }
|
运行代码,修改appsettings.json的值,就可以看到控制台打印出类似以下信息:
优化代码结构
在开发中经常会出现服务与选项一起注册的情况,为了优化代码结构,一般会为统一将一个服务的注册放到IServiceCollection的扩展方法中去。创建OrderServiceExtensions.cs,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection;
namespace OptionsDemo.Services { public static class OrderServiceExtensions { public static IServiceCollection AddOrderService(this IServiceCollection services, IConfiguration configuration) { services.Configure<OrderServiceOptions>(configuration.GetSection("OrderService")); services.AddSingleton<IOrderService, OrderService>(); return services; } } }
|
将Startup.ConfigureServices修改后代码如下:
1 2 3 4 5
| public void ConfigureServices(IServiceCollection services) { services.AddOrderService(Configuration); services.AddControllers(); }
|
运行之后可发现效果与上面一致
动态修改选项值
在注入选项之后,可动态对选项的值进行操作,这里以为MaxOrderCount的值增加100为例,修改OrderServiceExtensions,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection;
namespace OptionsDemo.Services { public static class OrderServiceExtensions { public static IServiceCollection AddOrderService(this IServiceCollection services, IConfiguration configuration) { services.Configure<OrderServiceOptions>(configuration.GetSection("OrderService")); services.PostConfigure<OrderServiceOptions>(options => { options.MaxOrderCount += 100; }); services.AddSingleton<IOrderService, OrderService>(); return services; } } }
|
运行代码,可发现获得到的值比appsettings.json里的值增加100