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{  

     if (services == null){             throw new ArgumentNullException(nameof(services));}      if (configureOptions == null){            throw new ArgumentNullException(nameof(configureOptions));}services.AddSingleton<IConfigureOptions<TOptions>>(new ConfigureNamedOptions<TOptions>(name, configureOptions));    return services;
}

而不指定nameConfigureConfigureAll方法,都只是一种简写形式,使用默认的name

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{      if (services == null){          throw new ArgumentNullException(nameof(services));}     if (config == null){        throw new ArgumentNullException(nameof(config));}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));}}
}

大家或许会有点疑问:“ 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 (options == null){               throw new ArgumentNullException(nameof(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 源码学习之 Options[1]:Configure相关推荐

  1. ASP.NET Core 源码学习之 Options[4]:IOptionsMonitor

    前面我们讲到 IOptions 和 IOptionsSnapshot,他们两个最大的区别便是前者注册的是单例模式,后者注册的是 Scope 模式.而 IOptionsMonitor 则要求配置源必须是 ...

  2. ASP.NET Core 源码学习之 Options[3]:IOptionsSnapshot

    2017-06-28 更新: OptionsSnapshot 已改为 OptionsManager 变更详情 IOptionsCache 已改为 IOptionsMonitorCache 变更详情 在 ...

  3. ASP.NET Core 源码学习之 Options[2]:IOptions

    在 上一篇 中,介绍了一下Options的注册,而使用时只需要注入 IOption 即可: public ValuesController(IOptions<MyOptions> opti ...

  4. ASP.NET Core 源码学习之 Logging[2]:Configure

    在上一章中,我们对 ASP.NET Logging 系统做了一个整体的介绍,而在本章中则开始从最基本的配置开始,逐步深入到源码当中去. 默认配置 在 ASP.NET Core 2.0 中,对默认配置做 ...

  5. ASP.NET Core 源码学习之Logging[1]:Introduction

    在ASP.NET 4.X中,我们通常使用 log4net, NLog 等来记录日志,但是当我们引用的一些第三方类库使用不同的日志框架时,就比较混乱了.而在 ASP.Net Core 中内置了日志系统, ...

  6. asp.net core源码飘香:Options组件

    简介: Options组件是一个小组件,但用的地方很多.它本质是将一个POCO类注册到容器中(主要在Startup中作为其他组件的配置功能提供),后续使用的时候就可以通过比如构造函数注入等获取到POC ...

  7. ASP.NET Core源码学习(一)Hosting

    ASP.NET Core源码的学习,我们从Hosting开始, Hosting的GitHub地址为:https://github.com/aspnet/Hosting.git 朋友们可以从以上链接克隆 ...

  8. ASP.NET Core 2.1 源码学习之 Options[1]:Configure

    配置的本质就是字符串的键值对,但是对于面向对象语言来说,能使用强类型的配置是何等的爽哉! 目录 ASP.NET Core 配置系统 强类型的 Options Configure 方法 Configur ...

  9. ASP.NET Core 源码学习之 Logging[3]:Logger

    上一章,我们介绍了日志的配置,在熟悉了配置之后,自然是要了解一下在应用程序中如何使用,而本章则从最基本的使用开始,逐步去了解去源码. LoggerFactory 我们可以在构造函数中注入 ILogge ...

最新文章

  1. storm源码之storm代码结构【译】
  2. python一般用来开发什么-python主要用来做什么?Python开发简单吗?
  3. 把一个质量为M0 的物体加速到 时间慢100倍 需要多大能量
  4. Python 学习笔记 (8)—— sys模块
  5. 记一次使用 removeEventListener 移除事件监听失败的经历
  6. MySQL 5.0-触发器
  7. axios队列 vue_vue axios全攻略
  8. LeetCode 1026. 节点与其祖先之间的最大差值(二叉树DFS)
  9. 基于JAVA+SpringMVC+Mybatis+MYSQL的集市预约管理系统
  10. 解决Uncaught (in promise) reason的问题
  11. 计算机网络系统有哪些核心技术,区块链核心技术体系架构的网络层主要包括什么...
  12. HTML简易自适应布局
  13. xxx(telnet, netstat): command not found
  14. python调用若快打码接口
  15. PTA——是不是太胖了
  16. 12.4.1 索引顺序存取方法文件 / ISAM文件
  17. android软件制作app下载地址,如何自己制作安卓apk软件?
  18. HCIA:动态路由路由协议RIP及DHCP
  19. 520表白html实现3D动态相册,换成女朋友照片
  20. 梯度提升树(GBDT)算法超详细版本

热门文章

  1. 使用ssh tunnel 来做代理或跳板
  2. Briefly unavailable for scheduled maintenance message after doing automatic upgrade
  3. iOS 集合的深复制与浅复制
  4. 用VirtualWifi软件实现无线网卡同时连接多个AP。
  5. ASP.NET Core启动地址配置方法及优先级顺序 | .NET 6 版本
  6. 图像处理技术之分辨率与压缩
  7. Win11手机应用大改!全新界面来袭
  8. C# 用IrisSkin4.dll美化你的WinForm
  9. 基于事件驱动架构构建微服务第10部分:在docker容器内运行单元测试
  10. 技术分享|明源云天际集成开放平台接口中心基于IdentityServer4的鉴权机制