前言

前面基本介绍了,官方对于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. text-align: justify 文本对齐

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. 【Springboot项目启动异常】项目启动,数据库连接异常

    今天使用Springboot 整合 MybatisPlus 准备写一个Demo,在项目启动时,频繁出错,在此记录整个问题的解决过程 问题如下图 人工翻译一遍,主要意思就是没有检测到数据库驱动,也就是说 ...

  3. hdu4882 水贪心

    题意:      给你n个任务,每个任务有两个权值,t[i],b[i],前面的是完成任务所需时间,后面的那个是个参数,每个任务完成的代价是完成当前任务总时间(之前的+现在的) sumt * b[i], ...

  4. 病毒木马查杀实战第011篇:QQ盗号木马之专杀工具的编写

    前言 由于我已经在<病毒木马查杀第004篇:熊猫烧香之专杀工具的编写>中编写了一个比较通用的专杀工具的框架,而这个框架对于本病毒来说,经过简单修改也是基本适用的,所以本文就不讨论那些重叠的 ...

  5. 逆向 stdio.h 函数库 fwrite 函数(调试版本)

    0x01 fwrite 函数 函数原型: size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) 函数功能:把 ...

  6. Windows核心编程 第十五章 在应用程序中使用虚拟内存

    第1 5章 在应用程序中使用虚拟内存 Wi n d o w s提供了3种进行内存管理的方法,它们是: • 虚拟内存,最适合用来管理大型对象或结构数组. • 内存映射文件,最适合用来管理大型数据流(通常 ...

  7. 【easycode】使用说明 & 示例

    语法 ## 是模板的注释,不会生成 # 全局的一些设置 $ 使用库函数或者调用全局定义的内容 常用方法 数据库表信息 ${tableInfo} comment 表备注 name 表名 fullColu ...

  8. 自定义Tomcat部署目录

    1.创建配置文件 在Tomcat安装目录中conf-->Catalina-->localhost目录下,创建项目访问请求路径.xml文件 内容如下: <Context path=&q ...

  9. Gridea博客无法载入CSS样式的解决办法

    今日在使用Gridea客户端更新博客的过程中,推送到远端仓库后内容显示正常,但是无法载入主题样式,就是没有载入CSS样式,折腾了一下午在搞懂问题出在哪里了,下面说一下自己的解决思路. 问题描述 首先, ...

  10. pysmiles:一个用于读写SMILES表达式的python库

    技术背景 SMILES表达式是化学里面常用的用于标定元素之间关系的字符串,旨在用最简短的语句来完整的表达一个分子体系内所蕴含的基本信息,比如元素.连接性以及连接属性等.由于SMILES表达式的定义种类 ...