ASP.NET Core 2.1 源码学习之 Options[1]:Configure 【转】
原文链接:https://www.cnblogs.com/RainingNight/p/strongly-typed-options-configure-in-asp-net-core.html
配置的本质就是字符串的键值对,但是对于面向对象语言来说,能使用强类型的配置是何等的爽哉!
目录
- ASP.NET Core 配置系统
- 强类型的 Options
- Configure 方法
- ConfigureNamedOptions
ASP.NET Core 配置系统
在ASP.NET 4.X中,通常将配置存储在 web.config
中,使用静态帮助类来获取这些配置,而对 web.cofng
中进行任何修改时,则会导致应用程序池的回收,这种实现方式并不是很友好。
因此,在ASP.NET Core中,对配置系统进行了重写,仍然使用的是基本的键值对,但是它们可以从多种格式的配置源中来获取,比如:命令行、环境变量、XML文件、JSON文件等等,你也可以编写自定义的配置源提供程序。
通常在Stratup
类的构造函数中对配置源进行设置:
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
首先创建了一个ConfigurationBuilder
,然后设置配置源,最终生成的Configuration
是所有配置源中的键值对组合。
你可以直接在程序中使用IConfigurationRoot
来读取配置,但是建议你使用强类型的Options
,这样在你想获取某个配置时,只需要注入对应的Options
,而不是获取整个配置。
强类型的 Options
Options is a framework for accessing and configuring POCO settings.
简单来说,Options 就是将一个 POCO 的配置类,通过在Startup
类中注册到容器中,在后续使用的时候使用构造函数注入来获取到POCO对象。我们将这种编程模式称为Options模式。
首先定义一个 Options:
public class MyOptions
{
public string DefaultValue { get; set; }
}
然后我们在对应的appsettings.json
中添加如下片段:
{
"MyOptions": {
"DefaultValue" : "first"
}
}
在Startup
中的ConfigureServices
方法中,进行服务的注册:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));
}
最后,便在控制器中注入IOptions<MyOptions>
,通过其Value
属性对MyOptions
进行访问:
[Route("api/[controller]")]
public class ValuesController : Controller
{
private readonly MyOptions _options;
public ValuesController(IOptions<MyOptions> options)
{
_options = options.Value;
}
[HttpGet]
public string Get()
{
return _options.DefaultValue;
}
}
Configure 方法
Options框架为我们提供了一系统的IServiceCollection
的扩展方法,方便我们的使用。
直接使用Action配置
// 最简单的注册方式
services.Configure<MyOptions>(o => o.DefaultValue = true);
// 指定具体名称
services.Configure<MyOptions>("my", o => o.DefaultValue = true);
// 配置所有实例
services.ConfigureAll<MyOptions>(o => o.DefaultValue = true);
通过配置文件进行配置
// 使用配置文件来注册实例
services.Configure<MyOptions>(Configuration.GetSection("Sign"));
// 指定具体名称
services.Configure<MyOptions>("my", Configuration.GetSection("Sign"));
// 配置所有实例
services.ConfigureAll<MyOptions>(Configuration.GetSection("Sign"));
PostConfigure方法
PostConfigure
方法在 Configure
方法之后执行,是2.0中新增加的。
services.PostConfigure<MyOptions>(o => o.DefaultValue = true);
services.PostConfigure<MyOptions>("smyign", o => o.DefaultValue = true);
services.PostConfigureAll<MyOptions>(o => o.DefaultValue = true);
源码解析
首先看一下IConfigureOptions
接口:
public interface IConfigureOptions<in TOptions> where TOptions : class
{
void Configure(TOptions options);
}
而Configure
扩展方法中便是为IConfigureOptions<>
注册了一个单例ConfigureNamedOptions<>
:
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, string name, Action<TOptions> configureOptions)
where TOptions : class
{
services.AddOptions();
services.AddSingleton<IConfigureOptions<TOptions>>(new ConfigureNamedOptions<TOptions>(name, configureOptions));
return services;
}
而不指定name
的Configure和ConfigureAll方法,都只是一种简写形式,使用默认的DefaultName
:
public static class Options
{
public static readonly string DefaultName = string.Empty;
}
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, Action<TOptions> configureOptions) where TOptions : class
=> services.Configure(Options.Options.DefaultName, configureOptions);
public static IServiceCollection ConfigureAll<TOptions>(this IServiceCollection services, Action<TOptions> configureOptions) where TOptions : class
=> services.Configure(name: null, configureOptions: configureOptions);
如上,Configure
方法其实就是为IConfigureOptions<>
注册了一个单例ConfigureNamedOptions<>
。
再看一下使用IConfiguration
进行配置的扩展方法:
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, string name, IConfiguration config)
where TOptions : class
{
services.AddOptions();
services.AddSingleton<IOptionsChangeTokenSource<TOptions>>(new ConfigurationChangeTokenSource<TOptions>(name, config));
return services.AddSingleton<IConfigureOptions<TOptions>>(new NamedConfigureFromConfigurationOptions<TOptions>(name, config));
}
可以看到,注册的实例变成了NamedConfigureFromConfigurationOptions
,而其本质依然是调用ConfigureNamedOptions
,只不过Action
的方法体变成了ConfigurationBinder.Bind()
:
public class NamedConfigureFromConfigurationOptions<TOptions> : ConfigureNamedOptions<TOptions>
where TOptions : class
{
public NamedConfigureFromConfigurationOptions(string name, IConfiguration config)
: base(name, options => ConfigurationBinder.Bind(config, options))
{
if (config == null)
{
throw new ArgumentNullException(nameof(config));
}
}
}
在上面的Configure
方法中,都调用了AddOptions
,我们来看一下:
public static IServiceCollection AddOptions(this IServiceCollection 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;
}
如上便是Options
系统的几个核心对象了,后续章节再来一一介绍。
大家或许会有点疑问:“ IConfigureOptions 中的 Configure 方法是在什么时候调用的呢 ”,这个且看下章分解。
ConfigureNamedOptions
ConfigureNamedOptions 实现了 IConfigureNamedOptions
,而 IConfigureNamedOptions
则是对 IConfigureOptions
的一个扩展,添加了Name
参数,这样,我们便可以为同一个 Options 类型注册多个独立的实例,在某些场景下则是非常有用的。有关Name
的使用,则会在 第三章 来讲。
public interface IConfigureNamedOptions<in TOptions> : IConfigureOptions<TOptions> where TOptions : class
{
void Configure(string name, TOptions options);
}
再看一下 ConfigureNamedOptions 的源码:
public class ConfigureNamedOptions<TOptions> : IConfigureNamedOptions<TOptions>, IConfigureOptions<TOptions> where TOptions : class
{
public ConfigureNamedOptions(string name, Action<TOptions> action)
{
Name = name;
Action = action;
}
public string Name { get; }
public Action<TOptions> Action { get; }
public virtual void Configure(string name, TOptions options)
{
if (Name == null || name == Name)
{
Action?.Invoke(options);
}
}
public void Configure(TOptions options) => Configure(Options.DefaultName, options);
}
ConfigureNamedOptions 本质上就是把我们注册的Action
包装成统一的Configure
方法,以方便后续创建Options
实例时,进行初始化。
总结
本文描述了在 .NET Core 配置系统中Options的配置及原理,在 下一章 来讲一下IOptions
的使用。
ASP.NET Core 2.1 源码学习之 Options[1]:Configure 【转】的更多相关文章
- ASP.NET Core 2.1 源码学习之 Options[1]:Configure
配置的本质就是字符串的键值对,但是对于面向对象语言来说,能使用强类型的配置是何等的爽哉! 目录 ASP.NET Core 配置系统 强类型的 Options Configure 方法 Configur ...
- ASP.NET Core 2.1 源码学习之 Options[3]:IOptionsMonitor
前面我们讲到 IOptions 和 IOptionsSnapshot,他们两个最大的区别便是前者注册的是单例模式,后者注册的是 Scope 模式.而 IOptionsMonitor 则要求配置源必须是 ...
- ASP.NET Core 2.1 源码学习之 Options[3]:IOptionsMonitor 【转】
原文链接:https://www.cnblogs.com/RainingNight/p/strongly-typed-options-ioptions-monitor-in-asp-net-core. ...
- ASP.NET Core 2.1 源码学习之 Options[2]:IOptions
在 上一章 中,介绍了Options的注册,而在使用时只需要注入 IOption<T> 即可: public ValuesController(IOptions<MyOptions& ...
- ASP.NET Core 2.1 源码学习之 Options[2]:IOptions 【转】
原文链接:https://www.cnblogs.com/RainingNight/p/strongly-typed-options-ioptions-in-asp-net-core.html 在 上 ...
- ASP.NET Core 源码学习之 Options[1]:Configure
配置的本质就是字符串的键值对,但是对于面向对象语言来说,能使用强类型的配置是何等的爽哉! 目录 ASP.NET Core 配置系统 强类型的 Options Configure 方法 源码解析 ASP ...
- ASP.NET Core 选项模式源码学习Options Configure(一)
前言 ASP.NET Core 后我们的配置变得更加轻量级了,在ASP.NET Core中,配置模型得到了显著的扩展和增强,应用程序配置可以存储在多环境变量配置中,appsettings.json用户 ...
- ASP.NET Core 选项模式源码学习Options IOptions(二)
前言 上一篇文章介绍IOptions的注册,本章我们继续往下看 IOptions IOptions是一个接口里面只有一个Values属性,该接口通过OptionsManager实现 public in ...
- ASP.NET Core 选项模式源码学习Options IOptionsMonitor(三)
前言 IOptionsMonitor 是一种单一示例服务,可随时检索当前选项值,这在单一实例依赖项中尤其有用.IOptionsMonitor用于检索选项并管理TOption实例的选项通知, IOpti ...
随机推荐
- POJ-3187 Backward Digit Sums---枚举全排列
题目链接: https://vjudge.net/problem/POJ-3187 题目大意: 输入n,sum,求1~n的数,如何排列之后,相邻两列相加,直到得出最后的结果等于sum,输出1~n的排列 ...
- 【洛谷4459】[BJOI2018] 双人猜数游戏(动态规划)
点此看题面 大致题意: 一直有两个数\(m,n\),已知\(s\le m\le n\),且\(Alice\)和\(Bob\)二个"最强大佬"各知道\(mn\)和\(m+n\).每轮 ...
- python web应用--web框架(三)
了解了WSGI框架,我们发现:其实一个Web App,就是写一个WSGI的处理函数,针对每个HTTP请求进行响应. 但是如何处理HTTP请求不是问题,问题是如何处理100个不同的URL. 每一个URL ...
- JSONObject数组排序工具类
依赖jar <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</a ...
- glocktop
glocktop: Display or print active GFS2 locks. Index of glocktop man page Read glocktop man page on L ...
- git常用命令(三)
====================================================================== 本地仓库操作 ====================== ...
- 第49章 在SRAM中调试代码—零死角玩转STM32-F429系列
第49章 在SRAM中调试代码 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/fir ...
- 1.vue脚手架搭建项目
前言: 在使用Vue-cli脚手架搭建项目之前,需要安装node.js和npm以及vue-cli. 开始搭建项目: 1.打开cmd win+R 2.转到要搭建的项目路径: g: cd Webapp/v ...
- 4、SpringBoot------邮件发送(2)
开发工具:STS 代码下载链接:https://github.com/theIndoorTrain/Springboot/tree/0d6194d6ea2d7f4e19791a3d3f3167f861 ...
- 谈谈JDK8中的字符串拼接
字符串拼接问题应该是每个Java程序员都熟知的事情了,几乎每个Java程序员都读过关于StringBuffer/StringBuilder来拼接字符串. 在大多数的教程中,也许你会看到用+号拼接字符串 ...