配置的原子结构就是单纯的键值对,并且键和值都是字符串,但是在真正的项目开发中我们一般不会单纯地以键值对的形式来使用配置。值得推荐的做法就是采用《.NET Core采用的全新配置系统[1]: 读取配置数据》最后演示的方式将相关的配置定义成一个Options类型,并采用与类型定义想匹配的结构来定义原始的配置,这样就能利用它们之间的映射关系将读取的配置数据绑定为Options对象,我们将这种编程模式称为“Options模式”。 [ 本文已经同步到《ASP.NET Core框架揭秘》之中]

目录
一、配置绑定
二、扩展方法AddOptions
三、扩展方法Configure
四、Options对象的创建

一、配置绑定

对于一个Options对象来说,如果我们将其数据成员(这里主要指属性成员)视为其子节点,那么一个Options对象同样具有树形层次化结构,这与通过Configuration对象表示的配置树在结构上并没有本质的区别。如果Options类型的数据成员定义与配置树结构具有匹配的结构,那么将后者绑定为一个对应类型的Options对象是一件很容易的事情,对于这种将一个Configuration对象绑定为对应Options对象的行为简称为“配置绑定”。

配置绑定让我们可以根据得到的Configuration对象生成相应的Options对象,相关的API定义在“Microsoft.Extensions.Configuration.Binder”这个NuGet包中,后者为IConfiguration接口定义了如下一个GetValue方法得到绑定生成的Options对象。在调用这个放过的时候,我们会创建一个空的Options对象并将其作为参数,该方法会将Configuration承载的配置数据绑定到Options对象上。

   1: public static class ConfigurationBinder

   2: {

   3:     public static void Bind(this IConfiguration configuration, object instance);

   4: }

配置绑定的目标类型可以是一个简单的基元类型,也可以是一个自定义数据类型,还可以是一个数组、集合或者字典类型。上述的这个Bind方法在进行配置绑定的过程,针对不同的目标类型,它会采用不同的策略。至于该方法具体的实现原理,我们会在后续的部分予以单独介绍,而目前介绍的重点是Options模式采用的API在背后是如何调用这个方法得到所需的Options对象的。

我们在回顾一下《.NET Core采用的全新配置系统[1]: 读取配置数据》演示的采用Options模式读取配置的例子。Options模式是对依赖注入的应用,我们知道针对依赖注入的编程只涉及两个方面,即注册相应的服务到ServiceCollection对象上,在利用后者创建相应的ServiceProvider来提供我们所需的服务对象。如下面的代码片段所示,Options模式最终的目的是利用ServiceProvider得到一个类型为IOptions<TOptions>的服务对象,后者的Value通过配置绑定生成的Options对象。为了能够得到所需的服务对象,它借助两个扩展方法AddOptions和Configure<TOptions>注册了必要的服务。

   1: IConfiguration config = ...;

   2: FormatOptions options = new ServiceCollection()

   3:     .AddOptions()

   4:     .Configure<FormatOptions>(config.GetSection("Format"))

   5:     .BuildServiceProvider()

   6:     .GetService<IOptions<FormatOptions>>()

   7:     .Value;

二、扩展方法AddOptions

依然Options对象最终是利用依赖注入的方式创建的一个类型为IOptions<TOptions>的服务对象得到的,我们就先来认识一下这个接口。这是一个泛型接口,泛型参数类型TOptions代码的正式Options对象对应的类型。IOptions<TOptions>接口的定义如下,它只有一个唯一的只读属性Value返回我们所需的Options对象。

   1: public interface IOptions<out TOptions> where TOptions: class, new()

   2: {

   3:     TOptions Value { get; }

   4: }

当我们调用ServiceCollection的AddOptions的时候,该方法仅仅是按照如下的方式针对该类型注册了一个服务而已,这个服务的真实类型为OptionsManager <TOptions> ,注册的服务采用的生命周期模式为Singleton。换句话说,配置绑定生成的Options对象最终返回的实际上是通过OptionsManager <TOptions> 创建的。

   1: public static IServiceCollection AddOptions(this IServiceCollection services)

   2: {

   3:     services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(OptionsManager<>)));

   4:     return services;

   5: }

如下所示的是 OptionsManager <TOptions> 类型的定义,我们可以看到它的构造函数接受一个元素类型为IConfigureOptions<TOptions>的集合作为参数,我们将实现了该接口的类型以及对应对象统称为ConfigureOptions<TOptions>。IConfigureOptions<TOptions>接口定义了一个唯一的Configure方法,该方法将一个Options对象作为输入参数。从定义可以看出一个ConfigureOptions<TOptions>对象的作用与一个类型为Action<TOptions>的委托对象,所以对于它的实现类型ConfigureOptions<TOptions>来说,对应的对象就直接通过一个Action<TOptions>对象来创建。

   1: public class OptionsManager<TOptions> : IOptions<TOptions> where TOptions: class, new()

   2: {

   3:     public OptionsManager(IEnumerable<IConfigureOptions<TOptions>> setups);

   4:     public virtual TOptions Value { get; }

   5: }

   6:  

   7: public interface IConfigureOptions<in TOptions> where TOptions: class

   8: {

   9:     void Configure(TOptions options);

  10: }

  11:  

  12: public class ConfigureOptions<TOptions>: IConfigureOptions<TOptions> where TOptions : class, new()

  13: {

  14:     public Action<TOptions> Action { get; private set; }

  15:     public ConfigureOptions(Action<TOptions> action)

  16:     {

  17:         this.Action = action;

  18:     }

  19:     public void Configure(TOptions options)

  20:     {

  21:         this.Action(options);

  22:     }

  23: }

Options对象的创建体现在 OptionsManager <TOptions>类型的Value属性上。该属性的实现非常简单,它先调用默认无参构造函数(Options类型必须具有一个默认无参构造函数)创建一个空的Options对象,在返回之前,它会将其递交给初始化时指定的ConfigureOptions<TOptions>对象进行逐个处理。毫无疑问,针对Bind方法的调用肯定是通过某个ConfigureOptions<TOptions>对象参与到整个流程之中的,具体的实现自然与另一个扩展方法Configure有关。

三、扩展方法Configure

Options模式仅仅涉及到针对ServiceCollection的两个扩展方法(AddOptions和Configure<TOptions>),前者将服务IOptions<TOptions>/ OptionsManager <TOptions>注册到ServiceCollection之上,后者又作了怎样的服务注册呢?

   1: public static IServiceCollection Configure<TOptions>(this IServiceCollection services, IConfiguration config) where TOptions: class

   2: {

   3:     return services.AddSingleton<IConfigureOptions<TOptions>>( new ConfigureFromConfigurationOptions<TOptions>(config));

   4: }

   5:  

   6: public class ConfigureFromConfigurationOptions<TOptions> :ConfigureOptions<TOptions> where TOptions : class

   7: {

   8:     public ConfigureFromConfigurationOptions(IConfiguration config) 

   9:      : base(options => config.Bind(options))

  10:     { }

  11: }

从上面的代码片段可以看出,当我们调用ServiceCollection的扩展方法Configure<TOptions>时,该方法会利用指定 的Configuration对象创建一个ConfigureFromConfigurationOptions对象,并以服务类型IConfigureOptions<TOptions>注册到ServiceCollection上,采用的生命周期模式为Singleton。至于类型ConfigureFromConfigurationOptions,它是上面介绍的ConfigureOptions<TOptions>类型的继承者,创建该对象指定的Action<TOptions>委托对象通过调用Configuration对象的扩展方法Bind最终实现了配置绑定。

四、Options对象的创建

Options编程模式的背后以两个注册到ServiceCollection的服务为核心,这两个服务对应的服务接口分别是IOptions<TOptions>和IConfigureOptions<TOptions>,前者直接提供最终绑定了配置数据的Options对象,后者则在Options对象返回之前对它实施相应的初始化工作。这个两个服务分别通过扩展方法AddOptions和Configure方法注册到指定的ServiceCollection之中,服务的真实类型分别是OptionsManager<TOptions>和ConfigureFromConfigurationOptions<TOptions>,后者派生于ConfigureOptions<TOptions>。下图所示的UML体现了Options模型中涉及的这些接口/类型以及它们之间的关系。

.NET Core采用的全新配置系统[3]: “Options模式”下的配置是如何绑定为Options对象的更多相关文章

  1. “Options模式”下的配置是如何绑定为Options对象

    “Options模式”下的配置是如何绑定为Options对象 配置的原子结构就是单纯的键值对,并且键和值都是字符串,但是在真正的项目开发中我们一般不会单纯地以键值对的形式来使用配置.值得推荐的做法就是 ...

  2. .NET Core采用的全新配置系统[10]: 配置的同步机制是如何实现的?

    配置的同步涉及到两个方面:第一,对原始的配置文件实施监控并在其发生变化之后从新加载配置:第二,配置重新加载之后及时通知应用程序进而使后者能够使用最新的配置.要了解配置同步机制的实现原理,先得从认识一个 ...

  3. .NET Core采用的全新配置系统[1]: 读取配置数据

    提到“配置”二字,我想绝大部分.NET开发人员脑海中会立马浮现出两个特殊文件的身影,那就是我们再熟悉不过的app.config和web.config,多年以来我们已经习惯了将结构化的配置定义在这两个文 ...

  4. .NET Core采用的全新配置系统[2]: 配置模型设计详解

    在<.NET Core采用的全新配置系统[1]: 读取配置数据>中,我们通过实例的方式演示了几种典型的配置读取方式,其主要目的在于使读者朋友们从编程的角度对.NET Core的这个全新的配 ...

  5. .NET Core采用的全新配置系统[4]: “Options模式”下各种类型的Options对象是如何绑定的?

    旨在生成Options对象的配置绑定实现在IConfiguration接口的扩展方法Bind上.配置绑定的目标类型可以是一个简单的基元类型,也可以是一个自定义数据类型,还可以是一个数组.集合或者字典类 ...

  6. Azure ARM模式下VNet配置中需要注意的几点事项

    虚拟网络的配置是所有公有云中非常重要的环节.把虚拟网络配置好,对整个系统的管理.维护,以及安全性都非常重要. 本文将介绍Azure在ARM模式下VNet配置中需要特别注意的几点. 一 Azure的VN ...

  7. Vmware在NAT模式下网络配置详解

    Vmware在NAT模式下网络配置详解 Linux中的网络配置对于接触Linux不久的小白菜来说,还是小有难度的,可能是不熟悉这种与windows系列迥然不同的命令行操作,也可能是由于对Linux的结 ...

  8. .NET Core采用的全新配置系统[5]: 聊聊默认支持的各种配置源[内存变量,环境变量和命令行参数]

    较之传统通过App.config和Web.config这两个XML文件承载的配置系统,.NET Core采用的这个全新的配置模型的最大一个优势就是针对多种不同配置源的支持.我们可以将内存变量.命令行参 ...

  9. .NET Core采用的全新配置系统[9]: 为什么针对XML的支持不够好?如何改进?

    物理文件是我们最常用到的原始配置的载体,最佳的配置文件格式主要由三种,它们分别是JSON.XML和INI,对应的配置源类型分别是JsonConfigurationSource.XmlConfigura ...

随机推荐

  1. JavaScript中fn()和return fn()

    看博客时,注意到return的重要性 参考:http://www.cnblogs.com/raoyunxiao/p/5644032.html 看似反常的例子: var i = 0; function ...

  2. 如何在nuget上传自己的包+搭建自己公司的NuGet服务器(新方法)

    运维相关:http://www.cnblogs.com/dunitian/p/4822808.html#iis 先注册一个nuget账号https://www.nuget.org/ 下载并安装一下Nu ...

  3. Vue-Router 页面正在加载特效

    Vue-Router 页面正在加载特效 如果你在使用 Vue.js 和 Vue-Router 开发单页面应用.因为每个页面都是一个 Vue 组件,你需要从服务器端请求数据,然后再让 Vue 引擎来渲染 ...

  4. CentOS 7配置LNMP开发环境及配置文件管理

    安装并配置MySQL 5.6 从CentOS从7.x开始默认使用MariaDB.MariaDB完全兼容MySQL,包括API和命令行.但是很多时候我们还是会想要安装MySQL,所以不能直接通过yum命 ...

  5. SQLServer 版本之八大方法搞清 "我是谁"

    你正在使用 SQL Server 的哪个版本? 贴士:作为一个SQL Server数据库管理者或维护.支持人员,应该会经常问自己这样一个问题:我当前SQL Server版本号是?当前版本已经有的累计更 ...

  6. C++ 拷贝构造函数和赋值运算符

    本文主要介绍了拷贝构造函数和赋值运算符的区别,以及在什么时候调用拷贝构造函数.什么情况下调用赋值运算符.最后,简单的分析了下深拷贝和浅拷贝的问题. 拷贝构造函数和赋值运算符 在默认情况下(用户没有定义 ...

  7. 【干货分享】流程DEMO-借款申请

    流程名: 借款申请   业务描述: 当员工个人在工作中需要进行借款时,通过此项流程提交借款申请,审批通过后,财务部进行款项支付.   流程相关文件: 流程包.xml WebService业务服务.xm ...

  8. 编译器开发系列--Ocelot语言3.类型名称的消解

    "类型名称的消解"即类型的消解.类型名称由TypeRef 对象表示,类型由Type 对象表示.类型名称的消解就是将TypeRef 对象转换为Type 对象. TypeResolve ...

  9. linux练习题

    观察系统当前进程的运行情况的命令是( ):A.freeB.dmesgC.topD.last 答案:http://hovertree.com/tiku/bjag/foxg5n0q.htm Linux系统 ...

  10. Orcale 三层嵌套分页代码

    select * from( select emp.*,rownum a from ( select * from emp ) emp where rownum<7) where a>3