ASP.NET Core的配置(1):读取配置信息

提到“配置”二字,我想绝大部分.NET开发人员脑海中会立马浮现出两个特殊文件的身影,那就是我们再熟悉不过的app.config和web.config,多年以来我们已经习惯了将结构化的配置信息定义在这两个文件之中。到了.NET

Core的时候,很多我们习以为常的东西都发生了改变,其中也包括定义配置的方式。总的来说,新的配置系统显得更加轻量级,并且具有更好的扩展性,其最大的特点就是支持多样化的数据源。我们可以采用内存的变量作为配置的数据源,也可以直接配置定义在持久化的文件甚至数据库中。

由于很多人都不曾接触过这个采用全新设计的配置系统,为了让大家对此有一个感官的认识,我们先从编程的角度对它作一个初体验。针对配置的API涉及三个对象,它们分别是Configuration、ConfigurationBuilder和ConfigurationProvider,配置模型中具有相应的接口来表示它们。这三个对象之间的关系很清晰,Configuration对象承载着在编程过程中使用的配置信息,ConfigurationProvider则是配置信息原始数据源的提供者,两者之间沟通由ConfigurationBuilder来完成,它利用ConfigurationProvider提取源数据将其转换为Configuration对象。

一、以键-值对的形式读取配置

虽然在大部分情况下的配置信息从整体来说都具有一个结构化的层次关系,但是“原子”配置项都以最简单的“键-值对”的形式来体现,并且键和值都是字符串,接下来我们会通过一个简单的实例来演示如何以键值对的形式来读取配置。我们创建一个针对ASP.NET

Core的控制台应用,并在project.json中按照如下的方式添加针对“Microsoft.Extensions.Configuration”这个NuGet包的依赖,配置模型就实现在这个包中。

   1: {
   2:   ...
   3:   "dependencies": {
   4:     "Microsoft.Extensions.Configuration": "1.0.0-rc1-final"
   5:   },
   6: }

假设我们的应用需要通过配置来设定日期/时间的显示格式,为此我们定义了如下一个DateTimeFormatSettings类,它的四个属性体现了DateTime对象的四种显示格式(分别为长日期/时间和短日期/时间)。

   1: public class DateTimeFormatSettings
   2: {
   3:     public string LongDatePattern { get; set; }
   4:     public string LongTimePattern { get; set; }
   5:     public string ShortDatePattern { get; set; }
   6:     public string ShortTimePattern { get; set; }
   7:     //其他成员
   8: }

我们希望通过配置的形式来控制由DateTimeFormatSettings的四个属性体现的日期/时间显示格式,所以我们为它定义了一个构造函数。如下面的代码片段所示,该构造函数具有一个IConfiguration接口类型的参数,它正式承载相关配置信息的Configuration对象。我们调用Configuration对象的索引并指定相应配置项的Key来得到其Value。

   1: public class DateTimeFormatSettings
   2: {
   3:     //其他成员
   4:     public DateTimeFormatSettings (IConfiguration configuration)
   5:     {
   6:         this.LongDatePattern     = configuration["LongDatePattern"];
   7:         this.LongTimePattern     = configuration["LongTimePattern"];
   8:         this.ShortDatePattern    = configuration["ShortDatePattern"];
   9:         this.ShortTimePattern    = configuration["ShortTimePattern"];
  10:     }
  11: }

要创建一个体现当前配置的DateTimeFormatSettings对象,我们必须向得到这个承载相关配置信息的Configuration对象。正如我们上面所说,Configuration对象是由ConfigurationBuilder创建的,而原始的配置信息则是通过相应的ConfigurationProvider来读取的,所以创建一个Configuration对象的正确编程方式是先创建一个ConfigurationBuilder对象,然后为之添加一个或者多个ConfigurationProvider对象,最后利用ConfigurationBuilder来创建我们需要的Configuration对象。

按照上述的编程模式,我们在一个控制台应用中编写了如下的程序。我们创建了一个类型为ConfigurationBuilder的对象,调用其Add方法添加的ConfigurationProvider是一个类型为MemoryConfigurationProvider的对象。顾名思义,MemoryConfigurationProvider利用内存中的对象来提供原始的配置信息,具体来说这些原始的配置信息保存在一个元素类型为KeyValuePair<string,

string>的集合之中。我们最终调用ConfigurationBuilder的Build方法获取用于创建DateTimeFormatSettings对象所需的Configuration。

   1: public class Program
   2: {
   3:     public static void Main(string[] args)
   4:     {
   5:         Dictionary<string, string> source = new Dictionary<string, string>
   6:         {
   7:             ["LongDatePattern"]     = "dddd, MMMM d, yyyy",
   8:             ["LongTimePattern"]     = "h:mm:ss tt",
   9:             ["ShortDatePattern"]    = "M/d/yyyy",
  10:             ["ShortTimePattern"]    = "h:mm tt"
  11:         };
  12:         IConfiguration configuration = new ConfigurationBuilder()
  13:                 .Add(new MemoryConfigurationProvider(source))
  14:                 .Build();
  15:  
  16:         DateTimeFormatSettings settings = new DateTimeFormatSettings(configuration);
  17:         Console.WriteLine("{0,-16}: {1}", "LongDatePattern", settings.LongDatePattern);
  18:         Console.WriteLine("{0,-16}: {1}", "LongTimePattern", settings.LongTimePattern);
  19:         Console.WriteLine("{0,-16}: {1}", "ShortDatePattern", settings.ShortDatePattern);
  20:         Console.WriteLine("{0,-16}: {1}", "ShortTimePattern", settings.ShortTimePattern);
  21:     }
  22: }

为了验证根据配置创建的DateTimeFormatSettings对象与配置原始数据之间的关系,我们将它的四个属性输出于控制台上。当这个程序执行之后将在控制台上产生如下所示的输出,可以看出它正是我们提供的配置的真实反映。

   1: LongDatePattern : dddd, MMMM d, yyyy
   2: LongTimePattern : h:mm:ss tt
   3: ShortDatePattern: M/d/yyyy
   4: ShortTimePattern: h:mm tt

二、 读取结构化的配置

真实项目中涉及的配置大都具有一个结构化的层次结构,所以在配置模型中的Configuration对象同样具有这样的结构。结构化的配置具有一个树形层次结构,而一个Configuration对象表示的是组成这棵配置树的某个节点,这棵配置树则可以通过作为根节点的Configuration对象来体现。体现为键值对的原子配置项一般至存在于作为叶子节点的Configuration对象中,非叶子节点的Configuration包含一组子节点,而每个子节点同样是一个Configuration对象。

接下来我们同样以实例的方式来演示如何定义并读取具有层次化结构的配置。我们依然沿用上一节的应用场景,现在我们不仅仅需要设置日期/时间的格式,还需要设置其他数据类型的格式,比如表示货币的Decimal类型。为此我们定义了如下一个CurrencyDecimalFormatSettings类,它的属性Digits和Symbol分别表示小数位数和货币符号,一个CurrencyDecimalFormatSettings对象依然是利用一个表示配置的Configuration对象来创建的。

   1: {
   2:     public int     Digits { get; set; }
   3:     public string  Symbol { get; set; }
   4:  
   5:     public CurrencyDecimalFormatSettings(IConfiguration configuration)
   6:     {
   7:         this.Digits = int.Parse(configuration["Digits"]);
   8:         this.Symbol = configuration["Symbol"];
   9:     }
  10: }

我们定义了另一个名为FormatSettings的类型来表示针对不同数据类型的格式设置。如下面的代码片段所示,它的两个属性DateTime和CurrencyDecimal分别表示针对日期/时间和货币数字的格式设置。FormatSettings依然具有一个参数类型为IConfiguration接口的构造函数,它的两个属性均在此构造函数中被初始化。值得注意的是初始化这两个属性采用的是当前Configuration的“子配置节”,通过指定配置节名称调用GetSection方法获得。

   1: public class FormatSettings
   2: {
   3:     public DateTimeFormatSettings            DateTime { get; set; }
   4:     public CurrencyDecimalFormatSettings     CurrencyDecimal { get; set; }
   5:  
   6:     public FormatSettings(IConfiguration configuration)
   7:     {
   8:         this.DateTime = new DateTimeFormatSettings(configuration.GetSection("DateTime"));
   9:         this.CurrencyDecimal = new CurrencyDecimalFormatSettings(configuration.GetSection("CurrencyDecimal"));
  10:     }
  11: }

在我们上面演示的实例中,我们通过以一个MemoryConfigurationProvider对象来提供原始的配置信息。由于承载原始配置信息的是一个元素类型为KeyValuePair<string,

string>的集合,所以原始配置在物理存储上并不具有树形化的层次结构,那么它如何能够最终提供一个结构化的Configuration对象呢?其实很简单,虽然MemoryConfigurationProvider对象只能将配置信息存储为简单的“数据字典”,但是如果将Configuration对象在配置树中体现的路径作为Key,这个数据字典在逻辑上实际上就具有了一棵树的结构。实际上MemoryConfigurationProvider就是这么做的,这体现在我们如下所示的程序之中。

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         Dictionary<string, string> source = new Dictionary<string, string>
   6:         {
   7:             ["Format:DateTime:LongDatePattern"]     = "dddd, MMMM d, yyyy",
   8:             ["Format:DateTime:LongTimePattern"]     = "h:mm:ss tt",
   9:             ["Format:DateTime:ShortDatePattern"]     = "M/d/yyyy",
  10:             ["Format:DateTime:ShortTimePattern"]     = "h:mm tt",
  11:  
  12:             ["Format:CurrencyDecimal:Digits"]     = "2",
  13:             ["Format:CurrencyDecimal:Symbol"]     = "$",
  14:         };
  15:         IConfiguration configuration = new ConfigurationBuilder()
  16:                 .Add(new MemoryConfigurationProvider(source))
  17:                 .Build();
  18:  
  19:         FormatSettings settings = new FormatSettings(configuration.GetSection("Format"));
  20:         Console.WriteLine("DateTime:");
  21:         Console.WriteLine("\t{0,-16}: {1}", "LongDatePattern", settings.DateTime.LongDatePattern);
  22:         Console.WriteLine("\t{0,-16}: {1}", "LongTimePattern", settings.DateTime.LongTimePattern);
  23:         Console.WriteLine("\t{0,-16}: {1}", "ShortDatePattern", settings.DateTime.ShortDatePattern);
  24:         Console.WriteLine("\t{0,-16}: {1}\n", "ShortTimePattern", settings.DateTime.ShortTimePattern);
  25:  
  26:         Console.WriteLine("CurrencyDecimal:");
  27:         Console.WriteLine("\t{0,-16}: {1}", "Digits", settings.CurrencyDecimal.Digits);
  28:         Console.WriteLine("\t{0,-16}: {1}", "Symbol", settings.CurrencyDecimal.Symbol);
  29:     }
  30: }

如上面的代码片段所示,创建MemoryConfigurationProvider对象采用的字典对象包含6个基本的配置项,为了让它们在逻辑上具有一个树形化层次结构,所以的Key实际上体现了每个配置项所在配置节在配置树中的路径,路径采用冒号(“:”)进行分割。改程序执行之后会在控制台上呈现如下所示的输出结果。

   1: DateTime:
   2:         LongDatePattern : dddd, MMMM d, yyyy
   3:         LongTimePattern : h:mm:ss tt
   4:         ShortDatePattern: M/d/yyyy
   5:         ShortTimePattern: h:mm tt
   6:  
   7: CurrencyDecimal:
   8:         Digits          : 2
   9:         Symbol          : $

三、将结构化配置直接绑定为对象

在真正的项目开发过程中,我们都不会直接使用直接读取的配置,而都倾向于像我们演示的两个实例一样通过创建相应的类型(比如DateTimeFormatSettings、CurrencyDecimalSettings和FormatSettings)来定义一组相关的配置选项(Option),我们将定义配置选项(Option)的这些类型称为Option类型。在上面演示的实例中,为了创建这些封装配置的对象,我们都是采用手工读取配置的形式,如果定义的配置项太多的话,逐条读取配置项其实是一项非常繁琐的工作。

对于一个对象来说,如果我们将它的属性视为它的子节点,一个对象同样具有类似于Configuration对象的树形层次化结构。如果我们根据某个Option类型的结构来定义配置,或者反过来根据配置的结构来定义这个Option类型,那么Option类型的属性成员将与某个配置节具有一一对应的关系,那么原则上我们可以自动将配置信息绑定为一个具体的Option对象。

ASP.NET
Core针对配置的Option模型(OptionModel)帮助我们实现了从配置到Option对象之间的绑定,接下来我们就对此做一个简单的演示。Option模型实现在“Microsoft.Extensions.OptionModel”这个NuGet包中,除此之外,我们需要采用依赖注入的方式来使用Option模型,所以我们需要按照如下的方式为应用添加针对相应的依赖。

   1: {
   2:   ...
   3:   "dependencies": {
   4:     "Microsoft.Extensions.OptionsModel"        : "1.0.0-rc1-final",
   5:     "Microsoft.Extensions.DependencyInjection"    : "1.0.0-rc1-final"
   6:   },
   7: }

借助于Option模型的自动绑定机制,我们无需再手工地读取配置信息,所以我们将FormatSettings、DateTimeFormatSettings和CurrencyDecimalSettings的构造函数删除,只保留其属性成员。在作为程序入口的Main方法中,我们采用如下的方式创建这个表示格式设置的FormatSettings对象。

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         Dictionary<string, string> source = new Dictionary<string, string>
   6:         {
   7:             ["Format:DateTime:LongDatePattern"] = "dddd, MMMM d, yyyy",
   8:             ["Format:DateTime:LongTimePattern"] = "h:mm:ss tt",
   9:             ["Format:DateTime:ShortDatePattern"] = "M/d/yyyy",
  10:             ["Format:DateTime:ShortTimePattern"] = "h:mm tt",
  11:  
  12:             ["Format:CurrencyDecimal:Digits"] = "2",
  13:             ["Format:CurrencyDecimal:Symbol"] = "$",
  14:         };
  15:         IConfiguration configuration = new ConfigurationBuilder()
  16:                 .Add(new MemoryConfigurationProvider(source))
  17:                 .Build()
  18:                 .GetSection("Format"));
  19:  
  20:         IOptions<FormatSettings> optionsAccessor = new ServiceCollection()
  21:             .AddOptions()
  22:             .Configure<FormatSettings>(configuration)
  23:             .BuildServiceProvider()
  24:             .GetService<IOptions<FormatSettings>>();
  25:  
  26:         FormatSettings settings = optionsAccessor.Value;
  27:  
  28:         Console.WriteLine("DateTime:");
  29:         Console.WriteLine("\t{0,-16}: {1}", "LongDatePattern",settings.DateTime.LongDatePattern);
  30:         Console.WriteLine("\t{0,-16}: {1}", "LongTimePattern",settings.DateTime.LongTimePattern);
  31:         Console.WriteLine("\t{0,-16}: {1}", "ShortDatePattern",settings.DateTime.ShortDatePattern);
  32:         Console.WriteLine("\t{0,-16}: {1}\n", "ShortTimePattern",settings.DateTime.ShortTimePattern);
  33:  
  34:         Console.WriteLine("CurrencyDecimal:");
  35:         Console.WriteLine("\t{0,-16}: {1}", "Digits",settings.CurrencyDecimal.Digits);
  36:         Console.WriteLine("\t{0,-16}: {1}", "Symbol",settings.CurrencyDecimal.Symbol);
  37:     }
  38: }

如上面的代码片段所示,我们创建一个ServiceCollection对象并调用扩展方法AddOptions注册于针对Option模型的服务。接下来我们调用Configure方法将FormatSettings这个Option类型与对应的Configuration对象进行映射。我们最后利用这个ServiceCollection对象生成一个ServiceProvider,并调用其GetService方法得到一个类型为IOptions<FormatSettings>的对象,后者的Value属性返回的就是绑定了相关配置的FormatSettings对象。

ASP.NET Core的配置(1):读取配置信息
ASP.NET Core的配置(2):配置模型详解
ASP.NET Core的配置(3): 将配置绑定为对象[上篇]
ASP.NET Core的配置(3): 将配置绑定为对象[下篇]
ASP.NET Core的配置(4):多样性的配置源[上篇]
ASP.NET Core的配置(4):多样性的配置源[中篇]
ASP.NET Core的配置(4):多样性的配置源[下篇]
ASP.NET Core的配置(5):配置的同步[上篇]
ASP.NET Core的配置(5):配置的同步[下篇]

作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文链接

时间: 2024-10-26 05:25:58

ASP.NET Core的配置(1):读取配置信息的相关文章

ASP.NET Core实现强类型Configuration读取配置数据

前言 实现读取JSON文件几种方式,在项目中采取老办法简单粗暴,结果老大过来一看,恩,这样不太可取,行吧那我就用.NET Core中最新的方式诺,切记,适合的才是最好的,切勿懒. .NET Core读取JSON文件通过读取文件方式  当我将VS2015项目用VS2017打开后再添加控制器,此时会报错如下: 此时我们应该在该项目中的.csproj中添加如下这一句才能解决此问题: <ItemGroup> <DotNetCliToolReference Include="Micros

ASP.NET Core实现类库项目读取配置文件

前言 之前继续在学习多线程方面的知识,忽然这两天看到博问中有个园友问到如何在.net core类库中读取配置文件,当时一下蒙了,这个提的多好,我居然不知道,于是这两天了解了相关内容才有此篇博客的出现,正常来讲我们在应用程序目录下有个appsettings.json文件对于相关配置都会放在这个json文件中,但是要是我建立一个类库项目,对于一些配置比如密钥或者其他需要硬编码的数据放在JSON文件中,在.net core之前配置文件为web.config并且有相关的类来读取节点上的数据,现如今在.n

ASP.NET Core配置教程之读取配置信息_实用技巧

提到"配置"二字,我想绝大部分.NET开发人员脑海中会立马浮现出两个特殊文件的身影,那就是我们再熟悉不过的app.config和web.config,多年以来我们已经习惯了将结构化的配置信息定义在这两个文件之中.到了.NET Core的时候,很多我们习以为常的东西都发生了改变,其中也包括定义配置的方式.总的来说,新的配置系统显得更加轻量级,并且具有更好的扩展性,其最大的特点就是支持多样化的数据源.我们可以采用内存的变量作为配置的数据源,也可以直接配置定义在持久化的文件甚至数据库中. 由

.NET Core采用的全新配置系统[1]: 读取配置数据

提到"配置"二字,我想绝大部分.NET开发人员脑海中会立马浮现出两个特殊文件的身影,那就是我们再熟悉不过的app.config和web.config,多年以来我们已经习惯了将结构化的配置定义在这两个文件之中.到了.NET Core的时代,很多我们习以为常的东西都发生了改变,其中也包括定义配置的方式.总的来说,新的配置系统显得更加轻量级,并且具有更好的扩展性,其最大的特点就是支持多样化的数据源.我们可以采用内存的变量作为配置的数据源,也可以直接配置定义在持久化的文件甚至数据库中.由于很多

ASP.NET Core的配置(4):多样性的配置来源[中篇]

我们在本篇文章中会介绍三种针对物理文件的ConfiguationProvider,它们分别是针对JSON文件的JsonConfiguationProvider,针对XML文件的XmlConfiguationProvider以及针对INI文件的IniConfiguationProvider.对于这三种文件类型(JSON.XML和INI)来说,JSON能够采用简单直观的格式表示具有不同结构的数据,所以它是作为配置最好的选择. 目录 MemoryConfigurationProvider Enviro

ASP.NET Core的配置(2):配置模型详解

在上面一章我们以实例演示的方式介绍了几种读取配置的几种方式,其中涉及到三个重要的对象,它们分别是承载结构化配置信息的Configuration,提供原始配置源数据的ConfigurationProvider,以及作为"中间人"的ConfigurationBuilder.接下来我们将会对由这三个核心对象组成的配置模型进行详细介绍,不过在此之前我们有必要来认识配置信息在不同载体中所体现出来的三种结构. 目录 一.配置的三种结构 逻辑结构 原始结构 物理结构 结构转换 二.Configura

ASP.NET Core的配置(3): 将配置绑定为对象[上篇]

出于编程上的便利,我们通常不会直接利用ConfigurationBuilder创建的Configuration对象读取某个单一配置项的值,而是倾向于将一组相关的配置绑定为一个对象,我们将后者称为Options对象.我们在本章第一节通过简单的实例演示了如何利用Options模型实现了配置数据向Options对象的绑定,现在我们对Options模型背后的实现原理进行详细介绍. 目录 一.ConfigurationBinder 二.绑定简单对象 三.绑定复杂对象 四.绑定集合对象 五.绑定字典对象 一

ASP.NET Core的配置(5):配置的同步[设计篇]

本节所谓的"配置同步"主要体现在两个方面:其一,如何监控配置源并在其变化的时候自动加载其数据,其目的是让应用中通过Configuration对象承载的配置与配置源的数据同步:其二.当Configuration对象承载的配置放生变换的时候如何向应用程序发送通知,最终让应用程序使用最新的配置. 一.配置与配置源的同步 配置模型提供了三个原生ConfigurationProvider(JsonConfigrationProvider.XmlConfigurationProvider和IniC

ASP.NET Core的配置(4):多样性的配置来源[下篇]

我们在上篇和中篇对配置模型中默认提供的各种ConfigurationProvider进行了深入详尽的介绍,如果它们依然不能满足项目中的配置需求,我们可以还可以通过自定义ConfigurationProvider来支持我们希望的配置来源.就配置数据的持久化方式来说,将配置存储在数据库中应该是一种非常常见的方式,接下来我们就是创建一个针对数据库的ConfigurationProvider,它采用最新的Entity Framework 7来完成数据库的存取操作. 目录 MemoryConfigurat