前言

前面基本介绍了,官方对于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 实践篇—————服务与配置之间[十一二]的更多相关文章

  1. 重新整理 .net core 实践篇—————服务的配置更新[十三]

    前言 前文讲述了,服务和配置直接的配合,这一节写一下,当配置文件修改了,每个服务如何感知自己的配置. 正文 服务感知到自己的配置发生变化,这就牵扯出两个东西: IoptionsMonitor<o ...

  2. 重新整理 .net core 实践篇—————3种配置验证[十四]

    前言 简单整理一些配置的验证. 正文 配置的验证大概分为3类: 直接注册验证函数 实现IValidteOptions 使用Microsoft.Extensions.Options.DataAnnota ...

  3. 重新整理 .net core 实践篇—————日志系统之战地记者[十五]

    前言 本节开始整理日志相关的东西.先整理一下日志的基本原理. 正文 首先介绍一下包: Microsoft.Extengsion.Logging.Abstrations 这个是接口包. Microsof ...

  4. 重新整理 .net core 实践篇————polly失败重试[三十四]

    前言 简单整理一下polly 重试. 正文 在开发程序中一般都有一个重试帮助类,那么polly同样有这个功能. polly 组件包: polly 功能包 polly.Extensions.Http 专 ...

  5. 重新整理 .net core 实践篇—————工作单元模式[二十六]

    前言 简单整理一下工作单元模式. 正文 工作单元模式有3个特性,也算是其功能: 使用同一上下文 跟踪实体的状态 保障事务一致性 工作单元模式 主要关注事务,所以重点在事务上. 在共享层的基础建设类库中 ...

  6. 重新整理 .net core 实践篇————配置应用[一]

    前言 本来想整理到<<重新整理.net core 计1400篇>>里面去,但是后来一想,整理 .net core 实践篇 是偏于实践,故而分开. 因为是重新整理,那么就从配置开 ...

  7. 重新整理 .net core 实践篇—————配置系统之简单配置中心[十一]

    前言 市面上已经有很多配置中心集成工具了,故此不会去实践某个框架. 下面链接是apollo 官网的教程,实在太详细了,本文介绍一下扩展数据源,和简单翻翻阅一下apollo 关键部分. apollo 服 ...

  8. 携程框架Apollo实现.NET Core微服务统一配置(测试环境-单机)

    Apollo实现.NET Core微服务统一配置(测试环境-单机) https://www.cnblogs.com/guolianyu/p/10065999.html 一.前言 注:此篇只是为测试环境 ...

  9. 重新整理 .net core 实践篇—————配置系统之军令状[七](配置文件)

    前言 介绍一下配置系统中的配置文件,很多服务的配置都写在配置文件中,也是配置系统的大头. 正文 在asp .net core 提供了下面几种配置文件格式的读取方式. Microsoft.extensi ...

随机推荐

  1. MySQL 储存引擎知识点

    一:MySQL 存储引擎概述 1.1 什么是存储引擎: '''MySQL中的数据用各种不同的技术存储在文件(或者内存)中.这些技术中的每一种技术都使用不同的存储机制.索引技巧.锁定水平并且最终提供广泛 ...

  2. 02- HTML网页基础知识与浏览器介绍

    1.认识网页 网页主要由文字,图像和超链接等元素构成.当然,除了这些元素,网页还可以包含音频,视频,以及flask等. 如图所示就是一个网页: 网页是如何形成的呢? 它是由前端人员写的代码,经过浏览器 ...

  3. 【Spring】 Spring如何解决循环依赖的问题?

    https://mp.weixin.qq.com/s/FtbzTMxHgzL0G1R2pSlh-A 通常来说,如果问Spring内部如何解决循环依赖,一定是单默认的单例Bean中,属性互相引用的场景. ...

  4. codeforces 229C

    题意:          http://codeforces.com/problemset/problem/229/C         给你一个全图,分成两部分,问你这两个途中一共有多少个三角形. 思 ...

  5. PAT 乙级 -- 1001 -- 害死人不偿命的(3n+1)猜想

    题目: 卡拉兹(Callatz)猜想: 对任何一个自然数n,如果它是偶数,那么把它砍掉一半:如果它是奇数,那么把(3n+1)砍掉一半.这样一直反复砍下去,最后一定在某一步得到n=1.卡拉兹在1950年 ...

  6. UVA11462年龄排序

    题意:       给你200w个人的年龄,年龄的范围是1-100,然后让你从小到大排序输出所有人的年龄,题目还特意强调输入文件限制25MB,题目内存限制2MB. 思路:      比较经典又简单的一 ...

  7. Day004 顺序结构

    顺序结构 JAVA的基本结构就是顺序结构,除非特别指明,否则就按照顺序一句一句执行. 顺序结构是最简单的算法结构. 语句与语句之间,是按照从上到下的顺序进行的,它是由若干个依次执行的处理步骤组成的,它 ...

  8. Java 进行时间处理

    Java 进行时间处理 一.Calendar (1).Calender介绍 Calendar的中文翻译是日历,实际上,在历史上有着许多种计时的方法.所以为了计时的统一,必需指定一个日历的选择.那现在最 ...

  9. JMeter关联陌生又熟悉

    JMeter关联是什么 JMeter关联,这几个字看着可能会有点陌生,实际上却是工作中经常会做的一件事情,尤其是接口自动化,它指的是把一个接口的响应作为另一个接口的参数,从而把接口关联起来. JMet ...

  10. Spring Security + OAuth2 + JWT 基本使用

    Spring Security + OAuth2 + JWT 基本使用 前面学习了 Spring Security 入门,现在搭配 oauth2 + JWT 进行测试. 1.什么是 OAuth2 OA ...