选项框架特性
- 支持单例模式读取配置
- 支持快照
- 支持配置变更通知
- 支持运行时动态修改选项值
设计原则
- 接口分离原则(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