重新整理 .net core 实践篇—————服务与配置之间[十一二]
前言
前面基本介绍了,官方对于asp .net core 设计配置和设计服务的框架的一些思路。看下服务和配置之间是如何联系的吧。
正文
服务:
public interface ISelfService
{
string ShowOptionName();
}
public class SelfService : ISelfService
{
IOptions<SelfServiceOption> _options;
public SelfService(IOptions<SelfServiceOption> options)
{
this._options = options;
}
public string ShowOptionName()
{
return _options.Value.Name;
}
}
实体配置类:
public class SelfServiceOption
{
public string Name { get; set; }
}
配置:
"SelfService": {
"name" : "zhangsan"
}
注册:
services.AddSingleton<ISelfService, SelfService>();
services.Configure<SelfServiceOption>(Configuration.GetSection("SelfService"));
获取调用在startup.Configure中:
var SelfService = app.ApplicationServices.GetService<ISelfService>();
Console.WriteLine(SelfService.ShowOptionName());
结果:
经过前面系列中,我们非常好的能够理解:services.Configure(Configuration.GetSection("SelfService"));
在反射通过属性获取值过程中就是SelfService+":"+属性名,对字典进行获取对应的值。
那么看下为什么我们在配置的时候需要IOptions options,也就是套一个Ioption呢?Ioption它是怎么实现的呢?它的机制是什么?
看下IOptions 的实现类OptionsManager:
public class OptionsManager<TOptions> : IOptions<TOptions>, IOptionsSnapshot<TOptions> where TOptions : class, new()
{
private readonly IOptionsFactory<TOptions> _factory;
private readonly OptionsCache<TOptions> _cache = new OptionsCache<TOptions>(); // Note: this is a private cache
/// <summary>
/// Initializes a new instance with the specified options configurations.
/// </summary>
/// <param name="factory">The factory to use to create options.</param>
public OptionsManager(IOptionsFactory<TOptions> factory)
{
_factory = factory;
}
/// <summary>
/// The default configured <typeparamref name="TOptions"/> instance, equivalent to Get(Options.DefaultName).
/// </summary>
public TOptions Value
{
get
{
return Get(Options.DefaultName);
}
}
/// <summary>
/// Returns a configured <typeparamref name="TOptions"/> instance with the given <paramref name="name"/>.
/// </summary>
public virtual TOptions Get(string name)
{
name = name ?? Options.DefaultName;
// Store the options in our instance cache
return _cache.GetOrAdd(name, () => _factory.Create(name));
}
}
那么我们调用Value 其实是调用IOptionsFactory的_factory.Create(name)。
查看一下,IOptionsFactory的实现类OptionsFactory,看里面的create方法。:
public class OptionsFactory<TOptions> : IOptionsFactory<TOptions> where TOptions : class, new()
{
private readonly IEnumerable<IConfigureOptions<TOptions>> _setups;
private readonly IEnumerable<IPostConfigureOptions<TOptions>> _postConfigures;
private readonly IEnumerable<IValidateOptions<TOptions>> _validations;
/// <summary>
/// Initializes a new instance with the specified options configurations.
/// </summary>
/// <param name="setups">The configuration actions to run.</param>
/// <param name="postConfigures">The initialization actions to run.</param>
public OptionsFactory(IEnumerable<IConfigureOptions<TOptions>> setups, IEnumerable<IPostConfigureOptions<TOptions>> postConfigures) : this(setups, postConfigures, validations: null)
{ }
/// <summary>
/// Initializes a new instance with the specified options configurations.
/// </summary>
/// <param name="setups">The configuration actions to run.</param>
/// <param name="postConfigures">The initialization actions to run.</param>
/// <param name="validations">The validations to run.</param>
public OptionsFactory(IEnumerable<IConfigureOptions<TOptions>> setups, IEnumerable<IPostConfigureOptions<TOptions>> postConfigures, IEnumerable<IValidateOptions<TOptions>> validations)
{
_setups = setups;
_postConfigures = postConfigures;
_validations = validations;
}
/// <summary>
/// Returns a configured <typeparamref name="TOptions"/> instance with the given <paramref name="name"/>.
/// </summary>
public TOptions Create(string name)
{
var options = new TOptions();
foreach (var setup in _setups)
{
if (setup is IConfigureNamedOptions<TOptions> namedSetup)
{
namedSetup.Configure(name, options);
}
else if (name == Options.DefaultName)
{
setup.Configure(options);
}
}
foreach (var post in _postConfigures)
{
post.PostConfigure(name, options);
}
if (_validations != null)
{
var failures = new List<string>();
foreach (var validate in _validations)
{
var result = validate.Validate(name, options);
if (result.Failed)
{
failures.AddRange(result.Failures);
}
}
if (failures.Count > 0)
{
throw new OptionsValidationException(name, typeof(TOptions), failures);
}
}
return options;
}
}
切开三段看:
第一段
var options = new TOptions();
foreach (var setup in _setups)
{
if (setup is IConfigureNamedOptions<TOptions> namedSetup)
{
namedSetup.Configure(name, options);
}
else if (name == Options.DefaultName)
{
setup.Configure(options);
}
}
上面是实例化我们的配置类。
namedSetup.Configure(name, options); 就是给我们selfServiceOption具体绑定。
这就要回到我们注入的地方了。
services.Configure<SelfServiceOption>(Configuration.GetSection("SelfService"));
来看下Configure 写的是什么,自己看具体的实现哈:
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, string name, IConfiguration config, Action<BinderOptions> configureBinder)
where TOptions : class
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
if (config == null)
{
throw new ArgumentNullException(nameof(config));
}
services.AddOptions();
services.AddSingleton<IOptionsChangeTokenSource<TOptions>>(new ConfigurationChangeTokenSource<TOptions>(name, config));
return services.AddSingleton<IConfigureOptions<TOptions>>(new NamedConfigureFromConfigurationOptions<TOptions>(name, config, configureBinder));
}
AddOptions方法 就是注册具体实现的。
public static IServiceCollection AddOptions(this IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(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;
}
重点来看下NamedConfigureFromConfigurationOptions 这个东西:
public class NamedConfigureFromConfigurationOptions<TOptions> : ConfigureNamedOptions<TOptions>
where TOptions : class
{
/// <summary>
/// Constructor that takes the <see cref="IConfiguration"/> instance to bind against.
/// </summary>
/// <param name="name">The name of the options instance.</param>
/// <param name="config">The <see cref="IConfiguration"/> instance.</param>
public NamedConfigureFromConfigurationOptions(string name, IConfiguration config)
: this(name, config, _ => { })
{ }
/// <summary>
/// Constructor that takes the <see cref="IConfiguration"/> instance to bind against.
/// </summary>
/// <param name="name">The name of the options instance.</param>
/// <param name="config">The <see cref="IConfiguration"/> instance.</param>
/// <param name="configureBinder">Used to configure the <see cref="BinderOptions"/>.</param>
public NamedConfigureFromConfigurationOptions(string name, IConfiguration config, Action<BinderOptions> configureBinder)
: base(name, options => config.Bind(options, configureBinder))
{
if (config == null)
{
throw new ArgumentNullException(nameof(config));
}
}
}
NamedConfigureFromConfigurationOptions 继承ConfigureNamedOptions是不是很眼熟了。
看下我们第一段调用部分:
if (setup is IConfigureNamedOptions<TOptions> namedSetup)
{
namedSetup.Configure(name, options);
}
后面就会调用NamedConfigureFromConfigurationOptions的Configure。
看下NamedConfigureFromConfigurationOptions的实例化方法,传递方法的第二个参数,options => config.Bind(options, configureBinder);
这个bind 是不是特别的眼熟,就是用来实体类绑定配置的,如有疑问可以看下前面几篇,讲述了绑定过程。
那么看下ConfigureNamedOptions的Configure(NamedConfigureFromConfigurationOptions继承ConfigureNamedOptions) 做了什么吧,我们来到ConfigureNamedOptions:
public Action<TOptions> Action { get; }
public ConfigureNamedOptions(string name, Action<TOptions> action)
{
Name = name;
Action = action;
}
public virtual void Configure(string name, TOptions options)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
// Null name is used to configure all named options.
if (Name == null || name == Name)
{
Action?.Invoke(options, Dependency);
}
}
这时候就会调用传递进来的Action:options => config.Bind(options, configureBinder),传递的这个options就是我们实例化的SelfServiceOption。
configureBinder 是一个配置哈,这是个配置的,下面第二个参数,如果没穿默认是一个()=>{};
如果需要传递可以这么写:
services.Configure<SelfServiceOption>(Configuration.GetSection("SelfService"), BinderOptions =>
{
BinderOptions.BindNonPublicProperties = true;
});
是不是很眼熟,就是设置可以绑定私有属性的选项。
这就把我们的SelfServiceOption 和 Configuration.GetSection("SelfService") 已经绑定了。
那么来看第二段:
foreach (var post in _postConfigures)
{
post.PostConfigure(name, options);
}
这个是什么呢? 这个是这样的,比如说我们的现在获取到了配置,得到了实体类SelfServiceOption,里面的name 是zhangsan。
这个PostConfigure 可以帮我们做后续修改,比如说我获取到了是zhangsan,但是呢,我想做一个判断,如果name 是zhangsan,就加一个后缀,name= name+"_a";
大概就是一些后续操作。
services.AddOptions<SelfServiceOption>().Configure((options) =>
{
if (options.Name == "zhangsan")
{
options.Name = "zhangsan_a";
}
});
效果如下:
第三段
if (_validations != null)
{
var failures = new List<string>();
foreach (var validate in _validations)
{
var result = validate.Validate(name, options);
if (result.Failed)
{
failures.AddRange(result.Failures);
}
}
if (failures.Count > 0)
{
throw new OptionsValidationException(name, typeof(TOptions), failures);
}
}
这一段很显然就是来验证我们的配置是否符合规格。源码就不解释了,实践篇注重实践。
看下怎么用的吧。
services.AddOptions<SelfServiceOption>().Configure((options) =>
{
if (options.Name == "zhangsan")
{
options.Name = "zhangsan_a";
}
}).Validate(options =>
{
return options.Name != "zhangsan_a";
});
然后就会报错。因为上面的逻辑是如果zhangsan,那么我把名字改成zhangsan_a,然后验证里面写名字不能等于zhangsan_a。
结
以上只是个人整理,如有错误,望请指点。
下一节,服务中的配置热更新。
重新整理 .net core 实践篇—————服务与配置之间[十一二]的更多相关文章
- 重新整理 .net core 实践篇—————服务的配置更新[十三]
前言 前文讲述了,服务和配置直接的配合,这一节写一下,当配置文件修改了,每个服务如何感知自己的配置. 正文 服务感知到自己的配置发生变化,这就牵扯出两个东西: IoptionsMonitor<o ...
- 重新整理 .net core 实践篇—————3种配置验证[十四]
前言 简单整理一些配置的验证. 正文 配置的验证大概分为3类: 直接注册验证函数 实现IValidteOptions 使用Microsoft.Extensions.Options.DataAnnota ...
- 重新整理 .net core 实践篇—————日志系统之战地记者[十五]
前言 本节开始整理日志相关的东西.先整理一下日志的基本原理. 正文 首先介绍一下包: Microsoft.Extengsion.Logging.Abstrations 这个是接口包. Microsof ...
- 重新整理 .net core 实践篇————polly失败重试[三十四]
前言 简单整理一下polly 重试. 正文 在开发程序中一般都有一个重试帮助类,那么polly同样有这个功能. polly 组件包: polly 功能包 polly.Extensions.Http 专 ...
- 重新整理 .net core 实践篇—————工作单元模式[二十六]
前言 简单整理一下工作单元模式. 正文 工作单元模式有3个特性,也算是其功能: 使用同一上下文 跟踪实体的状态 保障事务一致性 工作单元模式 主要关注事务,所以重点在事务上. 在共享层的基础建设类库中 ...
- 重新整理 .net core 实践篇————配置应用[一]
前言 本来想整理到<<重新整理.net core 计1400篇>>里面去,但是后来一想,整理 .net core 实践篇 是偏于实践,故而分开. 因为是重新整理,那么就从配置开 ...
- 重新整理 .net core 实践篇—————配置系统之简单配置中心[十一]
前言 市面上已经有很多配置中心集成工具了,故此不会去实践某个框架. 下面链接是apollo 官网的教程,实在太详细了,本文介绍一下扩展数据源,和简单翻翻阅一下apollo 关键部分. apollo 服 ...
- 携程框架Apollo实现.NET Core微服务统一配置(测试环境-单机)
Apollo实现.NET Core微服务统一配置(测试环境-单机) https://www.cnblogs.com/guolianyu/p/10065999.html 一.前言 注:此篇只是为测试环境 ...
- 重新整理 .net core 实践篇—————配置系统之军令状[七](配置文件)
前言 介绍一下配置系统中的配置文件,很多服务的配置都写在配置文件中,也是配置系统的大头. 正文 在asp .net core 提供了下面几种配置文件格式的读取方式. Microsoft.extensi ...
随机推荐
- C - The Suspects POJ - 1611(并查集)
Severe acute respiratory syndrome (SARS), an atypical pneumonia of unknown aetiology, was recognized ...
- 基于IMU与磁力计的手势提取手套-原理及其实现
手势提取依据所采用传感器的不同,可以分为基于视觉,基于惯性传感器,基于FSR,基于EMG传感器的方法.其中基于视觉的方法使用场景有限,且无法获取精确的手指关节角度:基于FSR的方法难以布置传感器且难以 ...
- Ubuntu下修改Nexus 5的boot.img--改user模式为debug模式
博客地址:http://blog.csdn.net/qq1084283172/article/details/52422205 在学习Android逆向的时候,总会用到Android的调试模式.一般情 ...
- hdu1024 最大m子序列和
题意: 给你一个序列n个数组成,然后让你在里面找到m个子序列,让这m个子序列的和最大. 思路: dp[i][j]表示的是第j个数字在第i个子序列时的当前最优值. dp[i][j] ...
- Linux下磁盘分区、卸载和磁盘配额
目录 一:查看磁盘信息 二:Linux磁盘分区 三:Linux分区的卸载 四:Linux磁盘配额 一:查看磁盘信息 fdisk : 这个命令是磁盘分区表操作工具,fdisk能将磁盘分区,同时也能为每个 ...
- 学习Canvas绘图与动画基础 canvas入门(一)
一.创建canvas 1 <!DOCTYPE html> 2 <html> 3 <head lang="en"> 4 <meta char ...
- Day001 电脑常用快捷键
电脑常用快捷键 Ctrl+C 复制 Ctrl+V 粘贴 Ctrl+A 全选 Ctrl+X 剪切 Ctrl+Z 撤销 Ctrl+S 保存 Alt+F4 关闭窗口(英雄联盟选英雄界面可以查看对面阵容(狗头 ...
- java之Map的使用
Map的实现类有很多,其中较为常见的有HashMap,HashTable,LinkedHashMap,TreeMap,下面分别对这几个类进行简单的分析: 1.HashMap HashMap的结构数组+ ...
- 获取汉字首字母并分组排列 PHP
1.代码class Character{ /** * 数组根据首首字母排序 */ /** * 二维数组根据首字母分组排序 * @param array $data 二维数组 * @param stri ...
- ppt技巧--字体变化
常见字体搭配 Nordri Tools