[ASP.NET Core 3框架揭秘] Options[1]: 配置选项的正确使用方式[上篇]
依赖注入不仅是支撑整个ASP.NET Core框架的基石,也是开发ASP.NET Core应用采用的基本编程模式,所以依赖注入十分重要。依赖注入使我们可以将依赖的功能定义成服务,最终以一种松耦合的形式注入消费该功能的组件或者服务中。除了采用依赖注入的形式消费承载某种功能的服务,还可以采用相同的方式消费承载配置数据的Options对象。
一、将配置绑定为Options对象
Options模式是一种采用依赖注入的方式来提供Options对象的编程方式,但这并不意味着我们会直接利用依赖注入框架来提供Options对象本身,因为利用依赖注入框架获取的是一个能够提供Options对象的IOptions<TOptions>对象,泛型参数TOptions表示的正是Options对象的类型。下面的演示实例利用IOptions<TOptions>服务来提供我们需要的Options对象,该对象由一个承载配置数据的IConfiguration对象绑定而成。简单起见,我们依然沿用《[ASP.NET Core 3框架揭秘] 配置[4]:将配置绑定为对象》定义的Profile作为基础的Options类型,下面先回顾相关类型的定义。
public class Profile : IEquatable<Profile>
{
public Gender Gender { get; set; }
public int Age { get; set; }
public ContactInfo ContactInfo { get; set; }
public Profile() { }
public Profile(Gender gender, int age, string emailAddress, string phoneNo)
{
Gender = gender;
Age = age;
ContactInfo = new ContactInfo
{
EmailAddress = emailAddress,
PhoneNo = phoneNo
};
}
public bool Equals(Profile other)
{
return other == null? false : Gender == other.Gender &&Age == other.Age && ContactInfo.Equals(other.ContactInfo);
}
} public class ContactInfo : IEquatable<ContactInfo>
{
public string EmailAddress { get; set; }
public string PhoneNo { get; set; }
public bool Equals(ContactInfo other)=> other == null ? false : EmailAddress == other.EmailAddress && PhoneNo == other.PhoneNo;
} public enum Gender
{
Male,
Female
}
下面通过一个简单的控制台应用来演示Options编程模式。在演示程序中定义了上面这些类型之后,我们创建承载一个Profile对象的配置文件profile.json。如下所示的代码片段就是这个JSON文件的内容,它提供了构成一个完整Profile对象的所有数据。为了使该文件能够在编译后自动复制到输出目录,我们需要将Copy to Output Directory属性设置为Copy Always。
{
"gender" : "Male",
"age" : "18",
"contactInfo": {
"emailAddress": "foobar@outlook.com",
"phoneNo" : "123456789"
}
}
下面编写代码来演示如何采用Options模式获取由配置文件提供的数据绑定生成的Profile对象。我们调用AddJsonFile扩展方法将针对JSON配置文件(profile.json)的配置源注册到创建的ConfigurationBuilder对象上,并利用它创建对应的IConfigurataion对象。
class Program
{
static void Main()
{
var configuration = new ConfigurationBuilder()
.AddJsonFile("profile.json")
.Build();
var profile = new ServiceCollection()
.AddOptions()
.Configure<Profile>(configuration)
.BuildServiceProvider()
.GetRequiredService<IOptions<Profile>>()
.Value;
Console.WriteLine($"Gender: {profile.Gender}");
Console.WriteLine($"Age: {profile.Age}");
Console.WriteLine($"Email Address: {profile.ContactInfo.EmailAddress}");
Console.WriteLine($"Phone No: {profile.ContactInfo.PhoneNo}");
}
}
上面创建一个ServiceCollection对象,在调用AddOptions扩展方法注册Options编程模式的核心服务后,可以将创建的IConfiguration对象作为参数调用Configure<Profile>扩展方法。Configure<TOptions>扩展方法相当于将提供的IConfiguration对象与指定的TOptions类型做了一个映射,在需要提供对应TOptions对象时,IConfiguration对象承载的配置数据会被提取出来并绑定生成返回的TOptions对象。
在调用IServiceCollection的BuildServiceProvider扩展方法得到作为依赖注入容器的IServiceProvider对象之后,可以直接调用其GetRequiredService<T>扩展方法来提供IOptions<Profile>对象,该对象的Value属性返回的就是指定IConfiguration对象绑定生成的Profile对象。我们将这个Profile对象承载的相关数据直接打印在控制台上,输出结果如下图所示,由此可以看出,通过Options模式得到的Profile对象承载的数据完全来源于配置文件。

二、提供具名的Options
针对同一个Options类型,通过IOptions<TOptions>服务在整个应用范围内只能提供一个单一的Options对象,但是在很多情况下我们需要利用多个同类型的Options对象来承载不同的配置。就演示实例中用来表示个人信息的Profile类型来说,应用程序中可能会使用它来表示不同用户的信息,如张三、李四和王五。为了解决这个问题,我们可以在添加IConfiguration对象与Options类型映射关系时赋予它们一个唯一标识,这个标识最终会被用来提取对应的Options对象。这种具名的Options对象由IOptionsSnapshot<TOptions>接口表示的服务提供。
同样,针对前面的演示实例,假设的应用需要采用Options模式提取承载不同用户信息的Profile对象,具体应该如何实现?由于采用JSON格式的配置文件来提供原始的用户信息,所以需要将针对多个用户的信息定义在profile.json文件中。我们通过如下形式提供了两个用户(foo和bar)的基本信息。
{
"foo": {
"gender": "Male",
"age": "18",
"contactInfo": {
"emailAddress": "foo@outlook.com",
"phoneNo": "123"
}
},
"bar": {
"gender": "Female",
"age": "25",
"contactInfo": {
"emailAddress": "bar@outlook.com",
"phoneNo": "456"
}
}
}
具名Options的注册和提取体现在如下所示的代码片段中。在调用IServiceCollection接口的Configure<TOptions>扩展方法时,我们将注册的映射关系命名为foo和bar,提供原始配置数据的IConfiguration对象也由原来的ConfigurationRoot对象变成它的两个子配置节。
class Program
{
static void Main()
{
var configuration = new ConfigurationBuilder()
.AddJsonFile("profile.json")
.Build(); var serviceProvider = new ServiceCollection()
.AddOptions()
.Configure<Profile>("foo", configuration.GetSection("foo"))
.Configure<Profile>("bar", configuration.GetSection("bar"))
.BuildServiceProvider(); var optionsAccessor = serviceProvider.GetRequiredService<IOptionsSnapshot<Profile>>();
Print(optionsAccessor.Get("foo"));
Print(optionsAccessor.Get("bar")); static void Print(Profile profile)
{
Console.WriteLine($"Gender: {profile.Gender}");
Console.WriteLine($"Age: {profile.Age}");
Console.WriteLine($"Email Address: {profile.ContactInfo.EmailAddress}");
Console.WriteLine($"Phone No: {profile.ContactInfo.PhoneNo}\n");
}
}
}
为了使用指定的用户名来提取对应的Profile对象,可以利用作为依赖注入容器的IServiceProvider对象得到IOptionsSnapshot<TOptions>服务,并将用户名作为参数调用其Get方法得到对应的Profile对象。程序运行后,针对两个不同用户的基本信息将以下图所示的形式输出到控制台上。

三、配置源的同步
通过《配置数据与数据源的实时同步》的介绍可知,配置模型不仅支持对配置源的监控,还可以在检测到更新之后及时加载新的配置数据,并通过一个IChangeToken对象对外发送通知。对于前面演示的两个实例来说,提供的Options对象都是由配置文件提供的数据绑定生成的,如果新的配置数据被重新加载之后能够提供与之匹配的Options对象,那么这将是最理想的编程模式,可以通过IOptionsMonitor<TOptions>服务来实现。
前面演示的第一个实例利用JSON文件定义了一个单一Profile对象的信息,下面对它做相应的修改来演示如何监控这个JSON文件,并在监测到文件改变之后及时提取新的配置信息生成新的Profile对象。如下面的代码片段所示,调用AddJsonFile扩展方法注册对应配置源时应将该方法的参数reloadOnChange设置为True,从而开启对对应配置文件的监控功能。
class Program
{
static void Main()
{
var configuration = new ConfigurationBuilder()
.AddJsonFile(path: "profile.json", optional: false, reloadOnChange: true)
.Build(); new ServiceCollection()
.AddOptions()
.Configure<Profile>(configuration)
.BuildServiceProvider()
.GetRequiredService<IOptionsMonitor<Profile>>()
.OnChange(profile =>
{
Console.WriteLine($"Gender: {profile.Gender}");
Console.WriteLine($"Age: {profile.Age}");
Console.WriteLine($"Email Address: {profile.ContactInfo.EmailAddress}");
Console.WriteLine($"Phone No: {profile.ContactInfo.PhoneNo}\n");
});
Console.Read();
}
}
在得到作为依赖注入容器的IServiceProvider对象之后,可以利用它得到IOptionsMonitor<TOptions>服务,该对象会接收到配置系统发出的关于配置被重新加载的通知,并在收到通知后重新生成Options对象。我们调用IOptionsMonitor<TOptions>对象的OnChange方法注册了一个类型为Action<TOptions>的委托对象,该委托对象会在接收到Options变化时自动执行,而作为输入的正是重新生成的Options对象。由于注册的委托对象会将新Profile对象的相关属性打印在控制台上,所以程序启动后针对配置文件的任何修改都会导致新的数据被打印在控制台上。例如,我们先后修改了年龄(25)和性别(Female),新的数据将按照下图所示的形式反映在控制台上。

具名Options同样可以采用类似的编程模式来实现配置的同步。在前面演示的提供具名Options的第二个实例的基础上,我们对程序做了如下修改。与之前不同的是,在利用IServiceProvider对象得到IOptionsMonitor<TOptions>服务之后,可以调用其OnChange方法注册的回调是一个Action<TOptions, String>对象,该委托对象的第二个参数表示的正是在注册IConfiguration对象与Options类型应用关系时指定的名称。
class Program
{
static void Main()
{
var configuration = new ConfigurationBuilder()
.AddJsonFile(path: "profile.json", optional: false, reloadOnChange: true)
.Build();
new ServiceCollection()
.AddOptions()
.Configure<Profile>("foo", configuration.GetSection("foo"))
.Configure<Profile>("bar", configuration.GetSection("bar"))
.BuildServiceProvider()
.GetRequiredService<IOptionsMonitor<Profile>>()
.OnChange((profile, name) =>
{
Console.WriteLine($"Name: {name}");
Console.WriteLine($"Gender: {profile.Gender}");
Console.WriteLine($"Age: {profile.Age}");
Console.WriteLine($"Email Address: {profile.ContactInfo.EmailAddress}");
Console.WriteLine($"Phone No: {profile.ContactInfo.PhoneNo}\n");
});
Console.Read();
}
}
由于通过调用OnChange方法注册的委托对象会将Options的名称和承载的数据打印在控制台上,所以控制台上输出的内容总是与配置文件的内容同步。例如,在程序启动后,我们分别修改了用户foo的年龄(25)和用户bar的性别(Male),新的内容将以图7-4所示的形式及时呈现在控制台上。

[ASP.NET Core 3框架揭秘] Options[1]: 配置选项的正确使用方式[上篇]
[ASP.NET Core 3框架揭秘] Options[2]: 配置选项的正确使用方式[下篇]
[ASP.NET Core 3框架揭秘] Options[3]: Options模型[上篇]
[ASP.NET Core 3框架揭秘] Options[4]: Options模型[下篇]
[ASP.NET Core 3框架揭秘] Options[5]: 依赖注入
[ASP.NET Core 3框架揭秘] Options[6]: 扩展与定制
[ASP.NET Core 3框架揭秘] Options[7]: 与配置系统的整合
[ASP.NET Core 3框架揭秘] Options[1]: 配置选项的正确使用方式[上篇]的更多相关文章
- [ASP.NET Core 3框架揭秘] Options[2]: 配置选项的正确使用方式[下篇]
四.直接初始化Options对象 前面演示的几个实例具有一个共同的特征,即都采用配置系统来提供绑定Options对象的原始数据,实际上,Options框架具有一个完全独立的模型,可以称为Options ...
- 《ASP.NET Core 3框架揭秘》博文汇总
在过去一段时间内,写了一系列关于ASP.NET Core 3相关的文章,其中绝大部分来源于即将出版的<ASP.NET Core 3框架揭秘>(博文只能算是"初稿",与书 ...
- ASP.NET Core 6框架揭秘实例演示[10]:Options基本编程模式
依赖注入使我们可以将依赖的功能定义成服务,最终以一种松耦合的形式注入消费该功能的组件或者服务中.除了可以采用依赖注入的形式消费承载某种功能的服务,还可以采用相同的方式消费承载配置数据的Options对 ...
- [ASP.NET Core 3框架揭秘] 依赖注入[5]: 利用容器提供服务
毫不夸张地说,整个ASP.NET Core框架是建立在依赖注入框架之上的.ASP.NET Core应用在启动时构建管道以及利用该管道处理每个请求过程中使用到的服务对象均来源于依赖注入容器.该依赖注入容 ...
- [ASP.NET Core 3框架揭秘] 文件系统[1]:抽象的“文件系统”
ASP.NET Core应用 具有很多读取文件的场景,比如配置文件.静态Web资源文件(比如CSS.JavaScript和图片文件等)以及MVC应用的View文件,甚至是直接编译到程序集中的内嵌资源文 ...
- [ASP.NET Core 3框架揭秘] 配置[1]:读取配置数据[上篇]
提到"配置"二字,我想绝大部分.NET开发人员脑海中会立即浮现出两个特殊文件的身影,那就是我们再熟悉不过的app.config和web.config,多年以来我们已经习惯了将结构化 ...
- [ASP.NET Core 3框架揭秘] 配置[2]:读取配置数据[下篇]
[接上篇]提到“配置”二字,我想绝大部分.NET开发人员脑海中会立即浮现出两个特殊文件的身影,那就是我们再熟悉不过的app.config和web.config,多年以来我们已经习惯了将结构化的配置定义 ...
- [ASP.NET Core 3框架揭秘] 配置[6]:多样化的配置源[上篇]
.NET Core采用的这个全新的配置模型的一个主要的特点就是对多种不同配置源的支持.我们可以将内存变量.命令行参数.环境变量和物理文件作为原始配置数据的来源.如果采用物理文件作为配置源,我们可以选择 ...
- [ASP.NET Core 3框架揭秘] 依赖注入[10]:与第三方依赖注入框架的适配
.NET Core具有一个承载(Hosting)系统,承载需要在后台长时间运行的服务,一个ASP.NET Core应用仅仅是该系统承载的一种服务而已.承载系统总是采用依赖注入的方式来消费它在服务承载过 ...
随机推荐
- sublime 插件安装packagecontrol
https://packagecontrol.io/installation 第一步: Installation Simple The simplest method of installation ...
- Pytest - 使用介绍
1. 概述 pytest是一个非常成熟的全功能的Python测试框架,主要特点有以下几点: 1.简单灵活,容易上手,文档丰富: 2.支持参数化,可以细粒度地控制要测试的测试用例: 3.能够支持简单的单 ...
- 【已解决】phpMyAdmin中导入mysql数据库文件时出错:您可能正在上传很大的文件,请参考文档来寻找解决办法
期间,用phpMyAdmin去导入90M左右的mysql数据库文件时出错: 您可能正在上传很大的文件,请参考文档来寻找解决方法. [解决过程] 1.很明显,是文件太大,无法导入.即上传文件大小有限制. ...
- JavaScript的一些SAO操作
IE判断检测 jQuery 在 1.9 版本之前,提供了一个浏览器对象检测的属性 使用率极高.但是在版本发布之后,大家钟爱的这个属性被无情的抛弃了.大家开始着手寻找.browser 的替代方案.于是各 ...
- Java RandomAccessFile用法(转载)
RandomAccessFile RandomAccessFile是用来访问那些保存数据记录的文件的,你就可以用seek( )方法来访问记录,并进行读写了.这些记录的大小不必相同:但是其大小和位置必须 ...
- Python--day24--继承
A_son.__bases__查看继承的父类是哪些 A. object是所有类的祖宗,所有的类都默认继承了object类. python中可以多继承 继承与抽象,先抽象再继承: example:
- Git 取消跟踪已版本控制的文件(亲测可行)
git 不再追踪文件改动 git update-index --assume-unchanged filePath git 恢复追踪文件改动 git update-index —no-assume-u ...
- Linux 内核总线属性
几乎 Linux 驱动模型中的每一层都提供一个添加属性的接口, 并且总线层不例外. bus_attribute 类型定义在 <linux/device.h> 如下: struct bus_ ...
- 文本框(代替input)输入长度限制、提示
<div class="inform_content_text"> <textarea name="name" placeholder=&qu ...
- tensorflow学习笔记——ResNet
自2012年AlexNet提出以来,图像分类.目标检测等一系列领域都被卷积神经网络CNN统治着.接下来的时间里,人们不断设计新的深度学习网络模型来获得更好的训练效果.一般而言,许多网络结构的改进(例如 ...