您现在的位置是:网站首页> 编程资料编程资料
.Net Core 中选项Options的具体实现_实用技巧_
2023-05-24
400人已围观
简介 .Net Core 中选项Options的具体实现_实用技巧_
.NetCore的配置选项建议结合在一起学习,不了解.NetCore 配置Configuration的同学可以看下我的上一篇文章 [.Net Core配置Configuration具体实现]
由代码开始
定义一个用户配置选项
public class UserOptions { private string instanceId; private static int index = 0; public UserOptions() { instanceId = (++index).ToString("00"); Console.WriteLine($"Create UserOptions Instance:{instanceId}"); } public string Name { get; set; } public int Age { get; set; } public override string ToString() => $"Name:{Name} Age:{Age} Instance:{instanceId} "; } public class UserOptions2 { public string Name { get; set; } public int Age { get; set; } public override string ToString() => $" Name:{Name} Age:{Age}"; } 定义json配置文件:myconfig.json
{ "UserOption": { "Name": "ConfigName-zhangsan", "Age": 666 } } 创建ServiceCollection
services = new ServiceCollection(); var configBuilder = new ConfigurationBuilder().AddInMemoryCollection().AddJsonFile("myconfig.json", true, true); var iconfiguration = configBuilder.Build(); services.AddSingleton(iconfiguration); 示例代码
services.Configure(x => { x.Name = "张三"; x.Age = new Random().Next(1, 10000); }); services.AddOptions ().Configure ((x, config) => { x.Name = config["UserOption:Name"]; x.Age = 100; }); ; services.PostConfigure (x => { x.Name = x.Name + "Post"; x.Age = x.Age; }); services.Configure ("default", x => { x.Name = "Default-张三"; x.Age = new Random().Next(1, 10000); }); services.Configure ("config", configuration.GetSection("UserOption")); using (var provider = services.BuildServiceProvider()) { using (var scope1 = provider.CreateScope()) { PrintOptions(scope1, "Scope1"); } //修改配置文件 Console.WriteLine(string.Empty); Console.WriteLine("修改配置文件"); var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "myconfig.json"); File.WriteAllText(filePath, "{\"UserOption\": { \"Name\": \"ConfigName-lisi\", \"Age\": 777}}"); //配置文件的change回调事件需要一定时间执行 Thread.Sleep(300); Console.WriteLine(string.Empty); using (var scope2 = provider.CreateScope()) { PrintOptions(scope2, "Scope2"); } Console.WriteLine(string.Empty); using (var scope3 = provider.CreateScope()) { PrintOptions(scope3, "Scope3"); } } static void PrintOptions(IServiceScope scope, string scopeName) { var options1 = scope.ServiceProvider.GetService >(); Console.WriteLine($"手动注入读取,IOptions,{scopeName}-----{ options1.Value}"); var options2 = scope.ServiceProvider.GetService >(); Console.WriteLine($"配置文件读取,IOptionsSnapshot,{scopeName}-----{ options2.Value}"); var options3 = scope.ServiceProvider.GetService >(); Console.WriteLine($"配置文件根据名称读取,IOptionsSnapshot,{scopeName}-----{ options3.Get("config")}"); var options4 = scope.ServiceProvider.GetService >(); Console.WriteLine($"配置文件读取,IOptionsMonitor,{scopeName}-----{ options4.CurrentValue}"); var options5 = scope.ServiceProvider.GetService >(); Console.WriteLine($"配置文件根据名称读取,IOptionsMonitor,{scopeName}-----{options5.Get("config")}"); var options6 = scope.ServiceProvider.GetService >(); Console.WriteLine($"Options2-----{options6.Value}"); }
代码运行结果
Create UserOptions Instance:01
手动注入读取,IOptions,Scope1----- Name:张三Post Age:6575 Instance:01
Create UserOptions Instance:02
配置文件读取,IOptionsSnapshot,Scope1----- Name:张三Post Age:835 Instance:02
Create UserOptions Instance:03
配置文件根据名称读取,IOptionsSnapshot,Scope1----- Name:ConfigName-zhangsan Age:666 Instance:03
Create UserOptions Instance:04
配置文件读取,IOptionsMonitor,Scope1----- Name:张三Post Age:1669 Instance:04
Create UserOptions Instance:05
配置文件根据名称读取,IOptionsMonitor,Scope1----- Name:ConfigName-zhangsan Age:666 Instance:05
Options2----- Name:ConfigName-zhangsan Age:100修改配置文件
Create UserOptions Instance:06手动注入读取,IOptions,Scope2----- Name:张三Post Age:6575 Instance:01
Create UserOptions Instance:07
配置文件读取,IOptionsSnapshot,Scope2----- Name:张三Post Age:5460 Instance:07
Create UserOptions Instance:08
配置文件根据名称读取,IOptionsSnapshot,Scope2----- Name:ConfigName-lisi Age:777 Instance:08
配置文件读取,IOptionsMonitor,Scope2----- Name:张三Post Age:1669 Instance:04
配置文件根据名称读取,IOptionsMonitor,Scope2----- Name:ConfigName-lisi Age:777 Instance:06
Options2----- Name:ConfigName-zhangsan Age:100手动注入读取,IOptions,Scope3----- Name:张三Post Age:6575 Instance:01
Create UserOptions Instance:09
配置文件读取,IOptionsSnapshot,Scope3----- Name:张三Post Age:5038 Instance:09
Create UserOptions Instance:10
配置文件根据名称读取,IOptionsSnapshot,Scope3----- Name:ConfigName-lisi Age:777 Instance:10
配置文件读取,IOptionsMonitor,Scope3----- Name:张三Post Age:1669 Instance:04
配置文件根据名称读取,IOptionsMonitor,Scope3----- Name:ConfigName-lisi Age:777 Instance:06
Options2----- Name:ConfigName-zhangsan Age:100
通过运行代码得到的结论
- Options可通过手动初始化配置项配置(可在配置时读取依赖注入的对象)、或通过IConfiguration绑定配置
- PostConfiger可在Configer基础上继续配置
- 可通过IOptionsSnapshot或IOptionsMonitor根据配置名称读取配置项,未指定名称读取第一个注入的配置
- IOptions和IOptionsMonitor生命周期为Singleton,IOptionsSnapshot生命周期为Scope
- IOptionsMonitor可监听到配置文件变动去动态更新配置项
问题
- IOptions,IOptionsSnapshot,IOptionsMonitor 如何/何时注入、初始化
- Options指定名称时内部是如何设置的
- Options如何绑定的IConfiguration
- IOptionsMonitor是如何同步配置文件变动的
配合源码解决疑惑
Configure注入
public static IServiceCollection Configure(this IServiceCollection services, Action configureOptions) where TOptions : class { return services.Configure(Microsoft.Extensions.Options.Options.DefaultName, configureOptions); } public static IServiceCollection Configure (this IServiceCollection services, string name, Action configureOptions) where TOptions : class { services.AddOptions(); services.AddSingleton((IConfigureOptions )new ConfigureNamedOptions (name, configureOptions)); return services; } public static IServiceCollection AddOptions(this IServiceCollection services) { services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(OptionsManager<>))); services.TryAdd(ServiceDescriptor.Scoped(typeof(IOptionsSnapshot<>), typeof(OptionsManager<>))); services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitor<>), typeof(OptionsMonitor<>))); services.TryAdd(ServiceDescriptor.Transient(typeof(IOptionsFactory<>), typeof(OptionsFactory<>))); services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitorCache<>), typeof(OptionsCache<>))); return services; }
通过上面的源码可以发现,Options相关类是在AddOptions中注入的,具体的配置项在Configure中注入。
如果不指定Configure的Name,也会有个默认的Name=Microsoft.Extensions.Options.Options.DefaultName
那么我们具体的配置项存到哪里去了呢,在ConfigureNamedOptions这个类中,在Configer函数调用时,只是把相关的配置委托存了起来:
public ConfigureNamedOptions(string name, Actionaction) { Name = name; Action = action; }
OptionsManager
private readonly ConcurrentDictionary> _cache = new ConcurrentDictionary >(StringComparer.Ordinal); public TOptions Value => Get(Options.DefaultName); public virtual TOptions Get(string name) { name = name ?? Options.DefaultName; return _cache.GetOrAdd(name, () => _factory.Create(name)); }
OptionsManager实现相对较简单,在查询时需要执行Name,如果为空就用默认的Name,如果缓存没有,就用Factory创建一个,否则就读缓存中的选项。
IOptions和IOptionsSnapshot的实现类都是OptionsManager,只是生命周期不同。
OptionsFactory
那么OptionsFactory又是如何创建Options的呢?我们看一下他的构造函数,构造函数将所有Configure和PostConfigure的初始化委托都通过构造函数保存在内部变量中
public OptionsFactory(IEnumerable> setups, IEnumerable > postConfigures) { _setups = setups; _postConfigures = postConfigures; }
接下来看Create(有删改,与本次研究无关的代码没有贴出来):
public TOptions Create(string name) { //首先创建对应Options的实例 TOptions val
相关内容
- 运用.net core中实例讲解RabbitMQ高可用集群构建_实用技巧_
- .Net Core 之AutoFac的使用_ASP.NET_
- asp.net core3.1cookie和jwt混合认证授权实现多种身份验证方案_实用技巧_
- 运用.NetCore实例讲解RabbitMQ死信队列,延时队列_实用技巧_
- 运用.net core中实例讲解RabbitMQ_实用技巧_
- .Net中异步任务的取消和监控的具体实现_实用技巧_
- 理解ASP.NET Core 中间件(Middleware)_实用技巧_
- .Net Core项目中NLog整合Exceptionless实例_实用技巧_
- .NET Core对象池的应用:扩展篇_实用技巧_
- .NET Core对象池的应用:设计篇_实用技巧_
点击排行
本栏推荐
