将代码中的配置信息抽取到文件中是良好的编程习惯,比如数据库的用户名、密码,我们如果切换数据库,直接修改配置文件即可,而无需修改源代码、重新编译工程。.Net Core提供了Microsoft.Extensions.Configuration
配置框架及一系列针对各种类型配置文件格式(如JSON、XML等)的扩展包,我们可以借助这些工具很容易实现配置文件加载、配置重载等功能。
Microsoft.Extensions.Configuration
包含基本的配置框架接口,我们需要使用NuGet进行安装。Binder包提供了自动绑定配置数据到对象的功能,通常也需要安装。
Install-Package Microsoft.Extensions.Configuration
Install-Package Microsoft.Extensions.Configuration.Binder
除此之外,我们还需要根据具体使用的配置方式,安装对应的配置提供者包。比如我们希望读取JSON格式的配置文件,那么就可以安装微软官方提供的NuGet包。
Install-Package Microsoft.Extensions.Configuration.Json
Install-Package Microsoft.Extensions.Configuration.Xml
Install-Package Microsoft.Extensions.Configuration.Ini
Install-Package Microsoft.Extensions.Configuration.EnvironmentVariables
Install-Package Microsoft.Extensions.Configuration.CommandLine
微软官方提供了读取JSON、XML和INI格式配置文件的NuGet包,也提供了读取环境变量、命令行参数的包,此外还提供了读取自家Azure KeyVault的NuGet包。对于其它配置文件格式或是某种配置中间件,我们可以寻找其它第三方包或自己编写配置提供者。
假设此时我们有如下JSON配置文件,我们打算使用配置框架读取其中的内容。
{
"username": "tom",
"password": "abc123",
"proxy": {
"host": "127.0.0.1",
"port": 8080
}
}
下面例子代码中,我们使用Configuration配置框架读取了上面的JSON文件,并取出其中的属性值。
using Microsoft.Extensions.Configuration;
namespace Gacfox.Demo.DemoNetCore
{
class Program
{
static void Main()
{
ConfigurationBuilder builder = new ConfigurationBuilder();
IConfigurationRoot root = builder
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("config.json")
.Build();
string username = root["username"];
string proxyHost = root["proxy:host"];
Console.WriteLine(username);
Console.WriteLine(proxyHost);
}
}
}
代码中,我们首先创建了ConfigurationBuilder
工具类,它是一个配置实例的构建器,随后我们通过它指定了配置文件的目录和文件名。另外这里值得注意的是我们的JSON数据是具有嵌套结构的,代码中我们使用了一种扁平化的语法读取了JSON数据中的属性。
AddJsonFile()
方法另一个比较常用的重载用法如下:
IConfigurationRoot root = builder
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("config.json", optional: false, reloadOnChange: true)
.Build();
optional
:该配置文件是否是可选的,如果指定false
则会在文件不存在时抛出异常reloadOnChange
:是否监测文件系统,在文件更新时自动重新加载配置在实际开发中,将配置的JSON、XML等数据绑定到对象是一个常见的做法,这样能够提升代码的抽象程度,使用也更为方便。将配置信息绑定到对象需要安装Microsoft.Extensions.Configuration.Binder
包。
这里我们还是使用前面提供的JSON数据,首先我们需要创建两个绑定配置信息的实体类。
Config.cs
namespace Gacfox.Demo.DemoNetCore
{
public class Config
{
public string Username { get; set; }
public string Password { get; set; }
public Proxy Proxy { get; set; }
}
}
Proxy.cs
namespace Gacfox.Demo.DemoNetCore
{
public class Proxy
{
public string Host { get; set; }
public int Port { get; set; }
}
}
在读取配置的代码中,我们使用在IConfigurationRoot
上调用Bind
方法,即可将配置信息绑定到实体类上。
Program.cs
using Microsoft.Extensions.Configuration;
namespace Gacfox.Demo.DemoNetCore
{
class Program
{
static void Main()
{
ConfigurationBuilder builder = new ConfigurationBuilder();
IConfigurationRoot root = builder
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("config.json")
.Build();
Config obj = new Config();
root.Bind(obj);
Console.WriteLine(obj.Username);
Console.WriteLine(obj.Proxy.Host);
}
}
}
微软官方提供了Options包,它将配置框架和DI结合起来,提供了实际软件开发中访问配置信息的一种最佳实践,也是ASP.NET Core工程中默认使用的读取配置信息的方式,这里我们简单介绍一下。Options包也是以扩展形式提供的,我们需要使用NuGet进行安装。
Install-Package Microsoft.Extensions.Options
当然,除了Options包,我们还需要上面介绍的Configuration框架包,以及DI框架的包,这里就不多介绍了。
DemoService.cs
using Microsoft.Extensions.Options;
namespace Gacfox.Demo.DemoNetCore
{
public class DemoService
{
private readonly IOptionsSnapshot<Config> configOptions;
public DemoService(IOptionsSnapshot<Config> configOptions)
{
this.configOptions = configOptions;
}
public void Foo()
{
Config config = configOptions.Value;
Console.WriteLine(config.Username);
}
}
}
在服务类代码中,我们使用构造函数注入了一个IOptionsSnapshot
对象,它是对具体的配置对象的一层封装,这里增加一层封装很好理解,它用于实现配置信息的重新加载等功能,而具体使用时我们需要将配置对象从其中取出。
实际上,注入时除了IOptionsSnapshot
,我们有3个选择:
IOptions
:配置信息仅初始化1次,应用启动后修改配置文件不会自动更新IOptionsSnapshot
:应用启动后修改配置文件,超出Scope范围后会读取到更新的数据IOptionsMonitor
:应用启动后修改配置文件,能立即读取到更新的数据实际开发中我们应该根据具体需求具体选择。
Program.cs
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Gacfox.Demo.DemoNetCore
{
class Program
{
static void Main()
{
ConfigurationBuilder builder = new ConfigurationBuilder();
IConfigurationRoot root = builder
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("config.json", optional: false, reloadOnChange: true)
.Build();
ServiceCollection collection = new ServiceCollection();
collection
.AddOptions()
.Configure<Config>(o => root.Bind(o));
collection.AddTransient<DemoService>();
using (ServiceProvider provider = collection.BuildServiceProvider())
{
DemoService demoService = provider.GetRequiredService<DemoService>();
demoService.Foo();
}
}
}
}
在Program.cs
中,我们首先初始化了配置框架,并指定了JSON文件等信息;然后我们初始化了DI框架,其中使用AddOptions()
方法指定了读取配置的回调函数,这样配置信息才可以自动注入,紧接着又将DemoService
加入IoC容器;最后,我们演示了从IoC容器中取出服务对象然后调用其中方法的过程。