重新整理 .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 ...
随机推荐
- 基于golang分布式爬虫系统的架构体系v1.0
基于golang分布式爬虫系统的架构体系v1.0 一.什么是分布式系统 分布式系统是一个硬件或软件组件分布在不同的网络计算机上,彼此之间仅仅通过消息传递进行通信和协调的系统.简单来说就是一群独立计算机 ...
- spring boot 实现redis 的key的过期监听,执行自己的业务
最近几天进一步了解了一下redis,发现了key的过期监听功能,实现方式如下: 在redis的配置文件 redis.conf 中找到"EVENT NOTIFICATION"模块, ...
- 【网络协议】OSI七层模型 和TCP/IP五层模型
OSI(Open System Interconnection)七层模型 TCP/IP 五层模型
- POJ2406简单KMP
题意: 给一个字符串,求最大的前缀循环周期,就是最小的循环节对应的最大的那个周期. 思路: KMP的简单应用,求完next数组后有这样的应用:next[i] :是最大循环节的第几位 ...
- Windows PE导出表编程3(暴力覆盖导出函数)
今天要尝试的导出表相关编程内容是:覆盖函数地址部分的指令代码. 这种覆盖技术,是将AddressOfFunctions指向的地址空间指令字节码实施覆盖,这种技术又繁衍出两种: 暴力覆盖,即将所有的代码 ...
- CTFHub-easy_search
easy_search 玩了好些天,今天做道题找找状态,明天开始肝了 打开是一个登录框 用amdin/admin尝试了一下,提示登陆失败 这里肯定不会是暴力破解,我猜是sql注入,试了万能密码or 1 ...
- C#/VB.NET 自定义动画路径
PPT中的动画效果可分为已有内置动画以及自定义动画.设置内置动画,只需直接指定动画效果类型即可.本文主要介绍如何实现自定义动画,即自定义形状动作线性路径.附C#及VB.NET代码供参考. 程序运行环境 ...
- 【vue-08】vuex
vuex的作用 简单理解,就是将多个组件共享的变量统一放到一个地方去管理,比如用户登录时的数据token. 快速上手 安装:npm install vuex 首先,我们在src文件夹下创建一个文件夹: ...
- RabbitMQ实现延时消息的两种方法
目录 RabbitMQ实现延时消息的两种方法 1.死信队列 1.1消息什么时候变为死信(dead-letter) 1.2死信队列的原理 1.3 代码实现 1.4死信队列的一个小坑 2 .延时插件 2. ...
- Jenkins + Docker + ASP.NET Core自动化部署
本来没想着要写这篇博客,但是在实操过程中,一个是被网络问题搞炸了心态(真心感觉网络能把人搞疯,别人下个包.下个镜像几秒钟搞定,我看着我的几KB小水管真是有苦说不出),另一个就是这里面坑还是有一些的,写 ...