[转]使用IConfigureNamedOptions和ConfigureAll配置命名选项
这是我上一篇关于在ASP.NET Core 2.x中使用多个强类型设置实例的后续文章。在文章的结尾,我介绍了命名选项的概念,该选项已添加到ASP.NET Core 2.0中。在本文中,我将详细介绍如何配置命名选项。我将特别关注:
- 命名选项如何与默认选项实例相关
- 使用以下命令配置命名选项时如何访问服务
IConfigureNamedOptions<T>
- 如何使用以下命令配置所有选项实例(命名和默认)
ConfigureAll<T>
- 使用以下命令配置所有选项实例时如何访问服务
IConfigureNamedOptions<T>
快速回顾命名选项
在上一篇文章中,我深入研究了名为options旨在解决的方案。命名选项提供了一个解决方案,您希望拥有多个强类型设置类的实例,每个实例都可以从DI容器中解析。
在上一篇文章中,我使用了一种场景,其中您想要使用WebHooks向Slack发送消息的任意数量的设置。例如,假设您具有以下强类型的设置对象:
public class SlackApiSettings
{
public string WebhookUrl { get; set; }
public string DisplayName { get; set; }
}
以及以下配置(存储在appsettings.json中),该配置将加载到IConfiguration
app上的对象中Startup
:
{
"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"
}
}
您可以使用以下命令将每个单独的通道绑定到新SlackApiSettings
实例Startup.ConfigureServices()
:
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()
),以及一个要绑定的配置节。您可以使用IOptionsSnapshot<>
接口方法及其Get(name)
方法访问以下设置:
public class SlackNotificationService
{
public SlackNotificationService(IOptionsSnapshot<SlackApiSettings> options)
{
// fetch the settings for each channel
SlackApiSettings devSettings = options.Get("Dev");
SlackApiSettings generalSettings = options.Get("General");
SlackApiSettings publicSettings = options.Get("Public");
}
}
值得记住的是,在
IOptionsSnapshot<T>
选项被请求时(每次请求一次)重新绑定选项。这与IOptions
在应用程序的生命周期内一次绑定选项不同。由于通常使用来公开命名选项IOptionsSnapshot<T>
,因此每个请求都将它们绑定一次。
命名选项与默认选项实例
您可以在同一应用程序中使用命名选项和默认选项,它们不会干扰。Configure()
不指定名称的调用将以默认选项为目标,例如:
public void ConfigureServices(IServiceCollection services)
{
// Configure named options
services.Configure<SlackApiSettings>("Dev", Configuration.GetSection("SlackApi:DevChannel"));
services.Configure<SlackApiSettings>("Public", Configuration.GetSection("SlackApi:PublicChannel"));
// Configure the default "unnamed" options
services.Configure<SlackApiSettings>(Configuration.GetSection("SlackApi:GeneralChannel"));
}
您可以使用或Value
上的属性来检索默认选项:IOptions<T>
IOptionsSnapshot<T>
public class SlackNotificationService
{
public SlackNotificationService(IOptionsSnapshot<SlackApiSettings> options)
{
// fetch the settings for each channel
SlackApiSettings devSettings = options.Get("Dev");
SlackApiSettings publicSettings = options.Get("Public");
// fetch the default unnamed options
SlackApiSettings defaultSettings = options.Value;
}
}
即使您没有在应用程序中显式使用命名选项,Options框架本身也会在后台使用命名选项。当您调用Configure<T>(section)
扩展方法(不提供名称)时,框架将使用Options.DefaultName
默认名称在后台调用扩展方法的命名版本:
public static IServiceCollection Configure<TOptions>(
this IServiceCollection services, IConfiguration config)
where TOptions : class
{
return services.Configure<TOptions>(Options.Options.DefaultName, config);
}
Options.DefaultName
设置为string.Empty
,因此以下两行具有相同的效果-它们配置默认选项对象:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<SlackApiSettings>(Configuration.GetSection("SlackApi:GeneralChannel"));
// Using string.Empty as the named options type
services.Configure<SlackApiSettings>(string.Empty, Configuration.GetSection("SlackApi:GeneralChannel"));
}
对于此帖子,这是要记住的重要事项-默认选项只是命名为具有特定名称的选项:string.Empty
。
对于本文的其余部分,我将展示一些配置命名选项的方法,特别是与默认的“未命名”选项相比。
使用以下命令将服务注入命名选项 IConfigureNamedOptions<T>
配置选项时,通常需要外部服务。我以前写过关于如何使用IConfigureOptions<T>
来配置选项时,访问服务。在上一篇文章中,我讨论了将这些服务注册为范围服务时需要注意的一些问题。
在所有这些文章中,我描述了如何使用来配置默认选项IConfigureOptions<T>
。您还可以实现一个等效接口来配置命名选项IConfigureNamedOptions<T>
:
public interface IConfigureNamedOptions<in TOptions> : IConfigureOptions<TOptions> where TOptions : class
{
void Configure(string name, TOptions options);
}
此接口有两种方法:
Configure(name, options)
-由接口直接实现Configure(options)
-由IConfigureOptions<T>
(继承)实现
在实现接口时,重要的是要了解Configure(name, options)
将为应用程序中实例化的选项对象的每个实例调用该接口T
。这包括所有命名选项,包括默认选项。由您决定在运行时当前正在配置哪个实例。
实施IConfigureNamedOptions<T>
特定命名的选项实例
我认为最容易理解的方法IConfigureNamedOptions<T>
是举一个例子。让我们考虑基于我之前描述的Slack WebHooks场景的情况。您的应用必须调用多个WebHook URL,这些URL在appsettings.json中配置,并绑定到的单独命名实例SlackApiSettings
。此外,您还有一个默认选项实例。这些都按照我之前所述进行配置:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<SlackApiSettings>("Dev", Configuration.GetSection("SlackApi:DevChannel"));
services.Configure<SlackApiSettings>("Public", Configuration.GetSection("SlackApi:PublicChannel"));
services.Configure<SlackApiSettings>(Configuration.GetSection("SlackApi:GeneralChannel"));
}
现在想象一下,命名实例的WebHook URL "Public"
事先未知,因此无法将其添加到appsettings.json中。相反,您可以使用一个单独的服务PublicSlackDetailsService
来查找URL:
public interface PublicSlackDetailsService
{
public string GetPublicWebhookUrl() => return "/some/url";
}
请注意,该
GetPublicWebhookUrl()
方法是同步的,而不是async
。在配置对象时,将在DI容器内进行选项配置,因此,执行异步操作(如调用远程端点)不是一个好地方。如果发现需要此功能,请考虑使用其他模式(例如,工厂对象)代替“选项”。
该PublicSlackDetailsService
服务已在ConfigureServices()
以下位置注册为Singleton :
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<PublicSlackDetailsService>();
}
重要提示:如果您需要使用范围服务来配置命名选项,请参阅我的上一篇文章。
通过实施IConfigureNamedOptions<T>
,您可以使用以下服务配置特定的命名选项实例("Public"
)PublicSlackDetailsService
:
public class ConfigurePublicSlackApiSettings: IConfigureNamedOptions<SlackApiSettings>
{
// inject the PublicSlackDetailsService directly
private readonly PublicSlackDetailsService _service;
public ConfigurePublicSlackApiSettings(PublicSlackDetailsService service)
{
_service = service;
}
// Configure the named instance
public void Configure(string name, SlackApiSettings options)
{
// Only configure the options if this is the correct instance
if (name == "Public")
{
options.WebhookUrl = _service.GetPublicWebhookUrl();
}
}
// This won't be called, but is required for the interface
public void Configure(SlackApiSettings options) => Configure(Options.DefaultName, options);
}
限制ConfigurePublicSlackApiSettings
只配置"Public"
命名实例很容易。对name
传递的参数进行简单检查Configure(name, options)
可以避免同时配置其他命名实例(例如"Dev"
)或默认实例(name
将是string.Empty
)。
还要注意的另一件事是,Configure(options)
方法(IConfigureOptions
接口要求)Configure(name, options)
使用名称委托给该方法Options.DefaultName
。从技术上讲,这实际上不是必需的:用于创建选项(OptionsFactory
)的选项基础结构总是Configure(name, options)
在其可用时优先调用。但是,所示示例应视为最佳实践。
最后要做的是将ConfigurePublicSlackApiSettings
类注册到DI容器中ConfigureServices()
:
public void ConfigureServices(IServiceCollection services)
{
// Configure the options objects using appsettings.json
services.Configure<SlackApiSettings>("Dev", Configuration.GetSection("SlackApi:DevChannel"));
services.Configure<SlackApiSettings>("Public", Configuration.GetSection("SlackApi:PublicChannel"));
services.Configure<SlackApiSettings>(Configuration.GetSection("SlackApi:GeneralChannel"));
// Add required service
services.AddSingleton<PublicSlackDetailsService>();
// Add named options configuration AFTER other configuration
services.AddSingleton<IConfigureOptions<SlackApiSettings>, ConfigurePublicSlackApiSettings>);
}
重要提示:请注意,您必须为注册
IConfigureOptions<T>
实例,不是一个IConfigureNamedOptions<T>
实例!同样,与所有选项配置一样,顺序很重要。
每当您请求SlackApiSettings
using 的实例时IOptionsSnapshot<T>.Get()
,该ConfigurePublicSlackApiSettings.Configure(name, options)
方法都会执行。该"Public"
实例将WebhookUrl
更新其属性,所有其他命名选项将被忽略。
现在,我之前说过,默认选项实例只是具有特殊名称的命名选项实例string.Empty
。我也说,IConfigureNamedOptions<T>
被调用的所有命名的设置。这包括使用来请求默认选项实例时IOptions<T>.Value
。ConfigurePublicSlackApiSettings
处理此作为name
传递到Configure(name, options)
会string.Empty
如此我们的代码优雅地忽略它,同其他任何命名选项。
使用以下命令配置所有选项对象 ConfigureAll<T>
到目前为止,在最近的帖子中,我已经展示了如何:
- 配置命名选项
- 使用服务来配置选项
IConfigureOptions
- 使用服务来配置命名选项
IConfigureNamedOptions
(本文)
我没有显示的一件事是如何一次配置所有选项:命名选项和默认选项。如果要绑定到配置部分或使用Action<>
,则最简单的方法是使用ConfigureAll()
扩展方法
public void ConfigureServices(IServiceCollection services)
{
// Configure ALL options instances, both named and default
services.ConfigureAll<SlackApiSettings>(Configuration.GetSection("SlackApi:GeneralChannel"));
services.ConfigureAll<SlackApiSettings>(options => options.DisplayName = "Unknown");
// Override values for named options
services.Configure<SlackApiSettings>("Dev", Configuration.GetSection("SlackApi:DevChannel"));
services.Configure<SlackApiSettings>("Public", Configuration.GetSection("SlackApi:PublicChannel"));
// Override values for default options
services.Configure<SlackApiSettings>(() => options.DisplayName = "default");
}
在此示例中,我们将请求的每个选项对象绑定到"SlackApi:GeneralChannel"
配置部分,并将设置DisplayName
为"Unknown"
。然后,根据所请求的选项实例的名称,可能会执行另一个配置步骤:
- 如果请求了默认实例(使用
IOptions<T>.Value
或IOptionsSnapshot<T>.Value
,则将DisplayName
其设置为"default"
) - 如果
"Dev"
请求命名实例,则该实例将绑定到"SlackApi:DevChannel"
配置部分 - 如果
"Public"
请求命名实例,则该实例将绑定到"SlackApi:PublicChannel"
配置部分 - 如果请求任何其他命名实例,则不会进行进一步的配置。
这就提出了另一个重要的观点:
您可以请求尚未显式注册的命名选项实例。
当您可以使用简单选项Action<>
或绑定到配置部分时,以这种方式配置所有选项很方便,但是如果需要使用类似服务,该PublicSlackDetailsService
怎么办?在这种情况下,您将返回到实施IConfigureNamedOptions<T>
。
配置所有选项实例时使用注入的服务
为了简单起见,我们将扩展前面描述的场景。而不是使用的PublicSlackDetailsService
设置WebhookUrl
只对"Public"
命名方案的实例,我们想象,我们需要设置值,每个命名的选项的实例,包括默认选项。幸运的是,我们需要做的就是if()
从先前的实现中删除该语句,并且我们已经到了很多:
public class ConfigureAllSlackApiSettings: IConfigureNamedOptions<SlackApiSettings>
{
// inject the PublicSlackDetailsService directly
private readonly PublicSlackDetailsService _service;
public ConfigurePublicSlackApiSettings(PublicSlackDetailsService service)
{
_service = service;
}
// Configure all instances
public void Configure(string name, SlackApiSettings options)
{
// we don't care which instance it is, just set the URL!
options.WebhookUrl = _service.GetPublicWebhookUrl();
}
// This won't be called, but is required for the interface
public void Configure(SlackApiSettings options) => Configure(Options.DefaultName, options);
}
剩下的就是ConfigureAllSlackApiSettings
在默认容器中注册。请记住,顺序对于选项配置很重要:如果要ConfigureAllSlackApiSettings
在其他配置之前运行,则应该Configure()
在ConfigureServices
;中的其他方法之前显示。否则应该出现在它们之后:
public void ConfigureServices(IServiceCollection services)
{
// Configure ALL options instances, both named and default
services.ConfigureAll<SlackApiSettings>(options => options.DisplayName = "Unknown");
// Override values for named options
services.Configure<SlackApiSettings>("Dev", Configuration.GetSection("SlackApi:DevChannel"));
services.Configure<SlackApiSettings>("Public", Configuration.GetSection("SlackApi:PublicChannel"));
// Add ALL options configuration AFTER other configuration (in this case)
services.AddSingleton<IConfigureOptions<SlackApiSettings>, ConfigureAllSlackApiSettings>);
}
随着Configure<T>()
,ConfigureAll<T>()
,IConfigureOptions<T>
,和IConfigureNamedOptions<T>
你有一个广泛的配置无论是默认选项,并在您的应用程序命名选项工具。IConfigureNamedOptions<T>
特别灵活-将配置应用于所有选项实例,子集或特定命名实例很容易。
但是,一如既往,最好选择最简单的方法来完成工作。不需要命名选项吗?不要使用它们。需要绑定到配置部分吗?只需使用Configure<T>()
。KISS制定规则,但很高兴知道您是否需要灵活性。
摘要
在本文中,我描述了默认选项对象如何是命名选项的特例,其名称为string.Empty
。我展示了如何通过实现来配置需要其他注入服务的选项IConfigureNamedOptions<T>
,以及如何限制也应用了哪些选项。
我还展示了如何使用ConfigureAll<T>()
扩展方法将配置应用于所有选项,包括命名实例和默认实例。最后,我展示了IConfigureNamedOptions<T>
在需要访问其他服务进行配置时如何使用相同的方法。
如果要实现IConfigureNamedOptions<T>
,请务必考虑所使用服务的生命周期。特别是,您需要采取额外的步骤来使用范围服务,如我在上一篇文章中所述。
转发自:https://andrewlock.net/configuring-named-options-using-iconfigurenamedoptions-and-configureall/
[转]使用IConfigureNamedOptions和ConfigureAll配置命名选项的更多相关文章
- [转]在.NET Core 2.x中将多个强类型设置实例与命名选项一起使用
自1.0版之前,ASP.NET Core已使用“ 选项”模式配置强类型设置对象.从那时起,该功能获得了更多功能.例如,引入了ASP.NET Core 1.1 IOptionsSnapshot,它允许您 ...
- php.ini 配置详细选项
php.ini 或 php3.ini 是 PHP 在启动时会读取的配置文件.该文件的存放路径为 /usr/local/lib/.在 PHP 3.x 版的配置文件为 php3.ini:而在 PHP 4. ...
- mudOS源码 options.h配置详细选项
/* options.h配置详细选项—————————————————————————-将 MudOS 下载解压以后可以在相应目录的根目录中找到 options.h 这个文件.如果修 改了这个文件,那 ...
- squid之------常用配置及选项
Squid常用命令 1.初始化在squid.conf里配置的cache目录 squid -z 2.对squid.conf排错,即验证squid.conf的语法和配置 squid -k parse 3. ...
- nacos注册中心配置命名服务不生效问题
nacos作为注册中心指定命名空间,配置如下: 但是启动之后发现服务都默认注册到了public这个命名空间下面,也就是指定的命名空间不生效 这是因为注册中心使用的命名空间的配置不是nacos.conf ...
- IDEA run/debug configurations中没有配置tomcat选项
原文链接:https://blog.csdn.net/qq_41016818/article/details/80871738 原因分析 没有配置tomcat插件 解决方法如下: file->s ...
- kettle配置命名参数
bat 调度文件如下 cd D:/Program Files/kettle700/data-integrationKitchen.bat /rep repository /dir /TEST /job ...
- ASP.NET Core 学习笔记 第五篇 ASP.NET Core 中的选项
前言 还记得上一篇文章中所说的配置吗?本篇文章算是上一篇的延续吧.在 .NET Core 中读取配置文件大多数会为配置选项绑定一个POCO(Plain Old CLR Object)对象,并通过依赖注 ...
- 避免在ASP.NET Core 3.0中为启动类注入服务
本篇是如何升级到ASP.NET Core 3.0系列文章的第二篇. Part 1 - 将.NET Standard 2.0类库转换为.NET Core 3.0类库 Part 2 - IHostingE ...
随机推荐
- 剑指Offer-37.二叉树的深度(C++/Java)
题目: 输入一棵二叉树,求该树的深度.从根结点到叶结点依次经过的结点(含根.叶结点)形成树的一条路径,最长路径的长度为树的深度. 分析: 递归求解左右子树的最大值即可,每遍历到一个结点,深度加1,最后 ...
- mysql分布式
一,复制,对数据进行备份,实现搞可用,提高吞吐量,实现高性能. 1,主从架构 2,多主架构 3,主主从从 4,主备 (实际用得多) 二,分片/分库分表 () 1,垂直拆分 1,垂直分表 2,垂直分库 ...
- go源码解析-Println的故事
本文主要通过平常常用的go的一个函数,深入源码,了解其底层到底是如何实现的. Println Println函数接受参数a,其类型为-interface{}.用过Java的对这个应该比较熟悉,Java ...
- PAT 1007 Maximum Subsequence Sum 最大连续子序列和
Given a sequence of K integers { N1, N2, …, NK }. A continuous subsequence is defined to be { Ni, Ni ...
- [Spring cloud 一步步实现广告系统] 3. 网关路由
Zuul(Router and Filter) WIKI: 传送门 作用 认证,鉴权(Authentication/Security) 预判(Insights) 压力测试(Stress Testing ...
- 根据excle说明文档建表
在Excel里整理好的表模型数据,可直接导入PowerDesigner.此功能通过PowerDesigner的脚本功能来实现,使用起来也简单.具体操作方法: 打开PowerDesigner,新 ...
- Eclipse:批量将Java源代码文件的编码从GBK转为UTF-8
很简单的几行代码,就可以批量将GBK格式的java文件转为UTF-8格式. 基本上所有文本文件的编码转换都可以采用这种方式. import java.io.File; import java.io.I ...
- [Spring]:java.lang.NoSuchMethodError: 'java.lang.String javax.annotation.Resource.lookup()'
错误信息 11月 05, 2019 9:32:15 下午 org.springframework.test.context.TestContextManager prepareTestInstance ...
- localStorage本地存储技术
localStorage 本地存储技术 本地存储技术,“不是永久的永久存储” 特点: 将数据存储到浏览器当中 存储的数据都是以字符串的形式存储的 和传统的数据库相比: 优点: 操作简单,容易学习 数据 ...
- vue跨域
比如 我要请求的地址是https://edu.51cto.com/center/seckill/index/get-seckill-data 首先去 config ==> index.js 添加 ...