.NET Core 2.x中使用Named Options处理多个强类型配置实例
来源: Using multiple instances of strongly-typed settings with named options in .NET Core 2.x
作者: Andrew Lock
译者: Lamond Lu
.NET Core从1.0版本开始,就已经开始使用Options
模式绑定强类型配置对象。从那时起到现在,这个特性已经获得了更多的功能。例如在.NET Core 1.1中引入的IOptionsSnapshot
类。使用这个类的好处是,当你的配置文件(例如: appsetting.json)发生变化时,它可以帮助我们自动刷新我们的强类型配置对象。
本篇博客中,我们将讨论在依赖注入容器中注册强类型配置的多个实例的几种方式。我将特别说明如何使用Named Options
方式来完成注入。
使用强类型配置
Options
模式将POCO对象和IConfiguration
对象绑定,从而实现强类型配置。因为这一过程我已经在之前一篇博文中介绍过,所以这里我就简述一下。
我们可以将强类型配置对象和配置绑定起来,并注入到你的服务中。
public class SlackApiSettings
{
public string WebhookUrl { get; set; }
public string DisplayName { get; set; }
}
你可以在Startup
类的ConfigureServices
中使用Configure
将强类型配置对象和配置中的一个节点绑定起来。
public void ConfigureServices(IServiceCollection services)
{
services.Configure<SlackApiSettings>(Configuration.GetSection("SlackApi"));
}
以上代码中,Configure
方法将你的配置和SlackApiSettings
对象绑定了起来。除了以上方式,Configure
方法还提供了一个参数为Action
的重载,所以你来可以使用如下的方式绑定配置。
public void ConfigureServices(IServiceCollection services)
{
services.Configure<SlackApiSettings>(x => x.DisplayName = "My Slack Bot");
}
你可以通过在服务中注入IOptions
对象来访问配置好的SlackApiSettings
对象。
public class SlackNotificationService
{
private readonly SlackApiSettings _settings;
public SlackNotificationService(IOptions<SlackApiSettings> options)
{
_settings = options.Value
}
public void SendNotification(string message)
{
// use the settings to send a message
}
}
你可以使用IOptions.Value
属性,获取到配置好的强类型对象。
除了以上方式,你还可以注入一个IOptionsSnapshot
接口对象。
使用IOptionsSnapshot
处理配置变化
到目前为止,我所展示的例子都是最典型的用法。但是当我们使用IOption
来读取强类型配置时,这意味着你的配置在程序生命周期中是不变的。即配置对象只会计算和绑定一次。假如你在程序运行过程中,更改了appSettings.json文件,程序读取配置时,依然会得到程序启动时的配置对象,而非你修改过之后的配置对象。
对我个人而言,对于大部分场景,使用IOption
已经能够解决所有问题。但是如果程序确实需要支持重新加载配置,我们还可以使用ASP.NET Core中的IOptionsSnapshot
。IOptionsSnapshot
和IOptions
使用方法一样,因此你无需在应用程序中执行任何额外的操作。你只需要使用IOptionsSnapshot.Value
属性读取配置对象即可。
public class SlackNotificationService
{
private readonly SlackApiSettings _settings;
public SlackNotificationService(IOptionsSnapshot<SlackApiSettings> options)
{
_settings = options.Value;
}
}
使用以上方式,如果你在程序启动后,修改了appSettings.json文件,IOptionsSnapshot
会在下一次请求时,更新配置值,你就能获取到新的配置值了。这里需要注意的是配置值的生命周期是Scoped
, 即在一次请求中,读取到的配置值都是一样的。
注意: 并不是所有的配置提供器都支持配置重新加载。文件类型的配置器都没有问题,但是环境变量配置器就不可以。
重新加载配置在某些情况下可能很有用,但IOptionsSnapshot
还有另一个技巧 - 命名选项(Named Options)。 我们很快就会介绍它们,但首先我们将看一下你可能偶尔遇到的问题,您需要拥有多个设置对象实例。
使用一个强类型配置对象的多个实例
IOption
的典型用例就是针对细粒度的配置。配置系统让你很容易的为特定服务注入小的,集中的POCO对象。但是如果你需要配置多个具有相同属性的配置对象时,应该怎么做的?例如,为了将消息发送到Slack
, 你需要一个Webhook Url和一个DisplayName. 当你调用SendNotification(message)
时,SlackNotificationService
会使用这些配置来向指定的Slack Channel中发送消息。
如果你想更新SlackNotificationService以允许你向多个频道发送消息,该怎么办?
public class SlackNotificationService
{
public void SendNotificationToDevChannel(string message) { }
public void SendNotificationToGeneralChannel(string message) { }
public void SendNotificationToPublicChannel(string message) { }
}
这里我已经为创建了向不同的频道发送消息的方法。但是问题是,我该如何为每个频道配置其对应的Webhook Url
和DisplayName
。
为了提供一些思路,这里我们假设我们的配置文件结构是这样的。
{
"SlackApi": {
"DevChannel" : {
"WebhookUrl": "https://hooks.slack.com/T1/B1/111111",
"DisplayName": "c0mp4ny 5l4ck b07"
},
"GeneralChannel" : {
"WebhookUrl": "https://hooks.slack.com/T2/B2/222222",
"DisplayName": "Company Slack Bot"
},
"PublicChannel" : {
"WebhookUrl": "https://hooks.slack.com/T3/B3/333333",
"DisplayName": "Professional Looking name"
}
}
为了在SlackNotificationService
中读取到响应的配置,这里有3种可行的方案。
1. 创建父类配置对象
第一种方式就是扩展SlackApiSettings
类,在其中包含各个频道的配置属性。
public class SlackApiSettings
{
public ChannelSettings DevChannel { get; set; }
public ChannelSettings GeneralChannel { get; set; }
public ChannelSettings PublicChannel { get; set; }
public class ChannelSettings
{
public string WebhookUrl { get; set; }
public string DisplayName { get; set; }
}
}
这里我创建了一个内嵌类ChannelSettings
, 并在SlackApiSettings
类中添加了针对3个Slack Channel的配置。这个新的配置类,正确反映了appSettings.json文件中的配置结构。
public void ConfigureServices(IServiceCollection services)
{
services.Configure<SlackApiSettings>(Configuration.GetSection("SlackApi"));
}
在SlackNotificationService
中,我们还是和之前一样注入的单个配置类对象
public class SlackNotificationService
{
private readonly SlackApiSettings _settings;
public SlackNotificationService(IOptions<SlackApiSettings> options)
{
_settings = options.Value;
}
}
这种配置方式的优点是易于理解,我们为每个Slack Channel配置了独立的强类型配置。缺点是如果要支持新的Slack Channel, 你每次都需要修改SlackApiSettings
类。
2. 为每个Channel配置创建单独的配置类
另外一种方法是我们可以独立配置每个Slack Channel。我们分开配置不同的Slack Channel, 并把他们注入到SlackNotificationService
服务中。
例如,我们将ChannelSettings
类变成一个抽象类
public abstract class ChannelSettings
{
public string WebhookUrl { get; set; }
public string DisplayName { get; set; }
}
然后每一个Slack Channel的配置类继承ChannelSettings
类。
public class DevChannelSettings: ChannelSettings { }
public class GeneralChannelSettings: ChannelSettings { }
public class PublicChannelSettings: ChannelSettings { }
为了配置不同的Slack Channel, 我们需要在程序启动时分别绑定不同的配置节点。
public void ConfigureServices(IServiceCollection services)
{
services.Configure<DevChannelSettings>(Configuration.GetSection("SlackApi:DevChannel"));
services.Configure<GeneralChannelSettings>(Configuration.GetSection("SlackApi:GeneralChannel"));
services.Configure<PublicChannelSettings>(Configuration.GetSection("SlackApi:PublicChannel"));
}
由于不同的Slack Channel拥有不同的配置,所以我们需要分开将他们注入到SlackNotificationService
中。
public class SlackNotificationService
{
private readonly DevChannelSettings _devSettings;
private readonly GeneralChannelSettings _generalSettings;
private readonly PublicChannelSettings _publicSettings;
public SlackNotificationService(
IOptions<DevChannelSettings> devOptions
IOptions<GeneralChannelSettings> generalOptions
IOptions<PublicChannelSettings> publicOptions)
{
_devSettings = devOptions;
_generalSettings = generalOptions;
_publicSettings = publicOptions;
}
}
这种方式的好处是当你需要添加新的Slack Channel配置时,你不需要去修改之前定义的配置类结构,你只需要添加一个针对新Slack Channel的配置类。但是它也让事情更加复杂了,你不仅需要为每个新的Slack Channel配置类绑定配置的节点, 还需要修改SlackNotificationService
的构造函数添加对新Slack Channel配置类的依赖。
3. 使用Named Options
第三种方案就是本文的主题Named Options
。Named Options
翻译过来就是命名配置,和它的字面意思一样,我们可以使用它为每个强类型配置对象起一个唯一的名称,并在使用时通过指定唯一名称来获取所需的强类型配置对象。
使用Named Options
, 你可以拥有同一个强类型配置类的不同实例,并独立配置他们。这意味着,我们可以继续使用本文开头所定义的SlackApiSettings
类。
public class SlackApiSettings
{
public string WebhookUrl { get; set; }
public string DisplayName { get; set; }
}
区别是,当我们配置强类型配置对象的代码有所不同。
public void ConfigureServices(IServiceCollection services)
{
services.Configure<SlackApiSettings>("Dev", Configuration.GetSection("SlackApi:DevChannel"));
services.Configure<SlackApiSettings>("General", Configuration.GetSection("SlackApi:GeneralChannel"));
services.Configure<SlackApiSettings>("Public", Configuration.GetSection("SlackApi:PublicChannel"));
}
我们使用的Configure
的2个参数的重载方法,其中第一个参数指定了一个唯一名称,第二个参数指定了配置文件中对应的节点名称。
为了使用这些命名配置(Named Options), 我们需要在SlackNotificationService
类的构造函数中注入IOptionSnapshot
对象,而不是我们之前使用的IOption
对象。IOptionsSnapshot.Get(name)
方法允许我们通过传入唯一名称,获取对应的强类型配置对象。
public class SlackNotificationService
{
private readonly SlackApiSettings _devSettings;
private readonly SlackApiSettings _generalSettings;
private readonly SlackApiSettings _publicSettings;
public SlackNotificationService(IOptionsSnapshot<SlackApiSettings> options)
{
_devSettings = options.Get("Dev");
_generalSettings = options.Get("General");
_publicSettings = options.Get("Public");
}
}
这种方式最大的好处是,当添加新的Slack Channel时,你不需要添加任何新的配置类,你只需要针对新的Slack Channel配置一个新的SlackApiSetting
对象即可。缺点是从SlackNotificationService
的构造函数上,你已经不知道它对应的配置节点是哪个了。
总结
在本篇博客中,我们介绍了如何在ASP.NET Core中使用强类型配置。然后我们讨论了如何在ASP.NET Core的依赖注入容器中添加强类型配置对象的多个实例。这里我们讲解了使用3种不同的方式
- 创建父类配置对象
- 为每个Channel配置创建单独的配置类
- 使用Named Options
.NET Core 2.x中使用Named Options处理多个强类型配置实例的更多相关文章
- web工程中web.xml元素加载顺序以及配置实例
简介 web.xml是web工程的配置文件,容器加载web工程时,会首先从WEB-INF中查询web.xml,并加载其中的配置信息,可以将web.xml认为是web工程的入口. web.xml中包含有 ...
- 如何为ASP.NET Core的强类型配置对象添加验证
原文: Adding validation to strongly typed configuration objects in ASP.NET Core 作者: Andrew Lock 译文: La ...
- ASP.NET Core 选项模式源码学习Options Configure(一)
前言 ASP.NET Core 后我们的配置变得更加轻量级了,在ASP.NET Core中,配置模型得到了显著的扩展和增强,应用程序配置可以存储在多环境变量配置中,appsettings.json用户 ...
- 避免在ASP.NET Core 3.0中为启动类注入服务
本篇是如何升级到ASP.NET Core 3.0系列文章的第二篇. Part 1 - 将.NET Standard 2.0类库转换为.NET Core 3.0类库 Part 2 - IHostingE ...
- ASP.NET Core 1.0 中使用 Swagger 生成文档
github:https://github.com/domaindrivendev/Ahoy 之前文章有介绍在ASP.NET WebAPI 中使用Swagger生成文档,ASP.NET Core 1. ...
- Entity Framework Core 2.0 中使用LIKE 操作符
Entity Framework Core 2.0 中使用LIKE 操作符 不定时更新翻译系列,此系列更新毫无时间规律,文笔菜翻译菜求各位看官老爷们轻喷,如觉得我翻译有问题请挪步原博客地址 本博文翻译 ...
- 在ASP.NET Core 2.0中使用CookieAuthentication
在ASP.NET Core中关于Security有两个容易混淆的概念一个是Authentication(认证),一个是Authorization(授权).而前者是确定用户是谁的过程,后者是围绕着他们允 ...
- ASP.NET Core 2.1中基于角色的授权
ASP.NET Core 2.1中基于角色的授权 授权是来描述用户能够做什么的过程.例如,只允许管理员用户可以在电脑上进行软件的安装以及卸载.而非管理员用户只能使用软件而不能进行软件的安装以及卸载.它 ...
- ASP.NET Core 2.2中的Endpoint路由
Endpoint路由 在ASP.NET Core 2.2中,新增了一种路由,叫做Endpoint(终结点)路由.本文将以往的路由系统称为传统路由. 本文通过源码的方式介绍传统路由和Endpoint路由 ...
随机推荐
- Spring基础知识备案
关于@Value注解不能为静态变量赋值的问题 // eg:(xxx.ooo.value=100) 以下这种方式,来自配置文件的属性值无法注入: public class XxxUtils { @Val ...
- SQL数据库的操作,表的操作
数据库定义语言(DDL):用于对数据库及数据库中的各种对象进行创建,删除,修改等操作 (1)create:用于创建数据库或数据库对象 (2)alter:用于对数据库或数据库对象进行修改 (3)drop ...
- Python 判断文件后缀
方法1, str的endswith方法: ims_path='data/market1501/Market-1501-v15.09.15/bounding_box_test/12312.jpg' im ...
- 通过Ajax来简单的实现局部刷新(主要为C#中使用的UpdatePanel控件和ScriptManager控件)
1. ScriptManager和UpdatePanel控件联合使用可以实现页面局部异步刷新的效果.UpdatePanel用来设置页面中局部异步刷新的区域,它必须依赖于ScriptManager,因为 ...
- Linux之环境搭建(二)
上一节介绍了PC机安装Ubuntu,本节来看看Ubuntu下安装VMWare,以及在VMWare中安装Windows10. 原本想使用免费的VMware Workstation Player 15,但 ...
- 如何给小学生讲清楚ECC椭圆曲线加密
对于RSA这套公私钥加密的思路,我以为我挺明白的,运用的娴熟自如. 当然现在RSA用的不多,而是基于ECC曲线来做签名验签,最大名鼎鼎的莫过于比特币. 可是前两天和别人讲代码,被问了ECC为什么可以用 ...
- Android中SDK工具集锦
来源:<Android 4 高级编程> Android提供的SDK中包含有很多用于设计.实现.调试应用程序的工具:比较重要的如下所述: 1. ADB工具 Android应用程序调试桥ADB ...
- 重启Oracle 服务
1.oracle用户进入sqlplus sqlplus /nolog connect /as sysdba startup exit 2.进去操作系统启动监听 lsnrctl start 3.使用we ...
- async与defer
<script>元素的几种常见属性: async 异步加载,立即下载,不应妨碍页面其他操作,标记为 async 的异步脚本并不保证按照指定的先后顺序执行,因此异步脚本不应该在加载期间修改 ...
- js的七大设计原则--迪米特原则
一.什么是迪米特原则 迪米特原则也叫最少知道原则,一个类应该对其他对象保持最少的了解.通俗来讲,就是一个类对自己依赖的类知道的越少越好.因为类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另 ...