ASP.NET Core - 配置系统之配置读取

时间:2022-12-13 00:38:17

一个应用要运行起来,往往需要读取很多的预设好的配置信息,根据约定好的信息或方式执行一定的行为。

配置的本质就是软件运行的参数,在一个软件实现中需要的参数非常多,如果我们以 Hard Code(硬编码)的方式写在应用代码中,这样配置就会很乱,而且后续也不容易修改。乱而多,而且不容易修改,这就需要一个统一管理的地方,最常见的方式就是配置文件,这个也是开发人员非常熟悉的方式。

通过配置文件设置好软件应用运行的各种参数之后,我们在开发过程中需要能够读取到配置文件的内容,根据配置内容进行软件逻辑的判断,实现完善的软件行为逻辑。这一篇就是介绍 .NET Core 框架下怎么使用配置系统,这也是 .NET Core 下的基础设施之一。

配置读取是配置系统最基本的操作,几乎是每个开发人员都会进行的操作,一个开发人员可能不清楚配置系统是怎么实现的,配置文件是怎么解析的,但一定都做过读取配置信息的操作。.NET Core 框架下对于配置系统的使用最终暴露出来的接口是 ·IConfiguration·,它是供配置数据的统一视图,配置读取就通过这个接口的实现来进行。

默认创建的 ASP .NET Core 框架模板项目中默认有一个 appsettings.json 配置文件,这个也是 ASP .NET Core 中最常用的配置文件,在配置文件中添加多一个 Settings 节点,内容如下:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "Settings": {
    "key1": "value1",
    "key2": 1,
    "key3": true,
    "key4": {
      "subKey1": "value",
      "subKey2": 1
    },
    "items": [ "item1", "item2", "item3" ]
  }
}

我们要读取配置文件中的内容,例如读取“AllowedHosts”对于的值,只需要将其注入到需要的服务类中即可使用,ASP.NET Core 模板项目中使用Web主机构建和管理应用,在使用主机默认配置的时候已经将 Iconfiguration 服务注册到依赖注入容器之中。

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private readonly IConfiguration _configuration;
    public WeatherForecastController(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    [HttpGet]
    public Task Get()
    {
        var allowedHosts = _configuration["AllowedHosts"];
        Console.WriteLine(allowedHosts);
        // 配置键不区分大小写
        var allowedHosts = _configuration["AllowedHosts"];
        Console.WriteLine(allowedHosts);
        return Task.CompletedTask;
    }
}

上面这种读取方式是索引器方式,最简单也是基本的方式,配置被加载到内存中是以键值对的方式存在的,我们可以通过配置键读取配置值,键是字符串,不区分大小写,读取出来的值都是字符串。

配置文件中配置值往往不止一层,就像上面 appsettings.json 文件中,Logging 节点下还有子节点,如果需要这种分层数据,可以使用 : 字符(英文冒号)分隔层次结构,例如获取上面配置键 Default 对于的配置值。

// 以 : 作为分隔符,表示层级结构
var defalutLogLevel = _configuration["Logging:LogLevel:Default"];

如果配置值是数组,需要读取数组中具体的某一个值,可以用该值在数组中的索引作为key,例如读取上面配置文件中的items数组中的 item2。

// 读取数组,可以用值在数组中的索引作为key
var item2 = _configuration["Settings:items:1"];

这种方式读取配置有挺多不方便的地方,例如配置值是数值型的时候,需要我们直接转换,例如一次只能读取到一个配置值。微软通过 Microsoft.Extensions.Configuration.Binder 中的ConfigurationBinder 类提供了一些 IConfiguration 的静态方法,用于获取配置值时进行自动转换和绑定。

(1) GetValue

通过ConfigurationBinder中的GetValue扩展方法,一样可以通过配置键从配置系统中读取对于的配置值。该方法有多个重载,支持通过泛型的方式进行数据类型转换,并且支持设置默认值。

var defaultLogLevel2 = _configuration.GetValue<string>("Logging:LogLevel:Default");
// 配置信息中不包含 "Logging:LogLevel:Default" 这个Key时,以默认值 "Error" 返回
var defaultLogLevel3 = _configuration.GetValue<string>("Logging:LogLevel:Default", "Error");

(2) GetSection

这样子有些情况下仍然无法满足我们的需要,某一些情况下我们会需要直接读取配置中的一部分节点,例如直接读取上面配置中的 LogLevel 部分。IConfiguration 中的 GetSection 方法可以通过 Key 直接读取某一个子节点。该方法的返回值是 IConfigurationSection 类型,永远不会返回 null,IConfigurationSection 实际上是一个 IConfiguration 的派生接口,也就是说我们还可以从 IConfigurationSection 再去获取我们需要的具体的配置值。

var section = _configuration.GetSection("Settings:key4");
var defaultLogLevel4 = section["Default"];

(3) Get

上面讲到通过 GetSection 获取到了配置文件中的一部分子节点,但是那样仍然不方便,还是需要一个一个去读取具体的值。可以通过 ConfigurationBinder.Get 扩展方法,将配置以强类型的方式绑定到对象上。

首先需要定义一个类来接收配置文件中的节点信息

public class KeyOptions
{
    public string subKey1 { get; set
    public int subKey2 { get; set; }
}

然后通过以下方式进行绑定:

var keyOption1 = _configuration.GetSection("Settings:key4").Get<KeyOptions>();

(4) Bind

ConfigurationBinder.Bind 扩展方法与 Get 方法类似,也是用于将配置绑定为强类型对象,不过Bind的方法是绑定到一个已实例化的对象上,需要提供一个已存在的对象。

var keyOption2 = new KeyOptions();
_configuration.GetSection("Settings:key4").Bind(keyOption2);

(5) Exists

上面说过,GetSection 方法获取配置中的子节点,返回值永远不会为 null。如果我们传入了一个不存在的key,肯定是获取不到对于的值的,这种情况下还是需要判断对于的子节点到底是不是真正存在的,这时候可以使用 Exists 方法。

var section = _configuration.GetSection("settings");
var exist = section.Exists();

除此之外,还有一个 GetChildren 方法,无需参数,用于获取到当前配置节点的所有直接子节点的集合。

以上就是 .NET Core 体系下配置系统读取配置的基本介绍,涉及到的类型最主要的是 IConfiguration 接口,除此之外还有上面提到的 IConfigurationSection 接口,以及 IConfigurationRoot 接口。

IConfigurationRoot 表示配置的根节点,是IConfiguration的派生接口,以下为接口的定义:

public interface IConfigurationRoot: IConfiguration{
   // 存放了当前应用程序的所有配置提供程序
   IEnumerable<IConfigurationProvider> Providers { get; }
    // 强制从配置提供程序中重载配置
    void Reload();
}

这里可以看到一个关键的属性 IEnumerable Providers,这个就是配置系统中的配置信息的来源,后面会仔细讲这个。而 Reload 方法中最关键的也是调用集合中各个 IConfigurationProvider 进行数据加载。

IConfigurationSection 表示配置中的子节点,也是 IConfiguration 的派生接口,以下为接口的定义:

public interface IConfigurationSection: IConfiguration{
    // 该子节点在其父节点中所表示的 key,即直接对应的key
    string Key { get; }
    // 该子节点在配置中的全路径(从根节点开始,到当前节点以:符号分隔的路径)
    string Path { get; }
    // 该子节点的 value。如果该子节点是叶子节点,则Value为该节点对于的值,若其下存在子节点,则其始终为 null
    string Value { get; set; }
}

IConfigurationSection 接口通过以上三个属性,结合 IConfiguration中的 GetChildren 方法来完整地表示的一个子节点,而 Exists 方法判断节点是否为空,就是针对 IConfigurationSection 中的 Value 属性和 GetChildren 方法来进行的。

public static class ConfigurationExtensions{
    public static bool Exists(thisIConfigurationSection section){
        if(section == null)
        {
            returnfalse;
        }
        returnsection.Value != null || section.GetChildren().Any();
    }
}


参考文章:

ASP.NET Core 中的配置 | Microsoft Learn
配置 - .NET | Microsoft Learn
理解ASP.NET Core - 配置(Configuration)



ASP.NET Core 系列:
目录:ASP.NET Core 系列总结
上一篇:ASP.NET Core - 依赖注入(四)