一、引言
在一个应用程序中,通常需要用到一些配置信息,例如数据库连接字符串、日志级别、缓存配置等等。通常情况下,这些配置信息是写在配置文件中的。在 .NET Core 中,配置是一个非常重要的模块,它提供了一个统一的方式来管理和访问应用程序的配置信息。本文将介绍 .NET Core 2.0 中的配置功能,并着重介绍了配置的热更新和配置的框架设计。
二、配置的基本用法
在 .NET Core 中,配置信息通常存储在 appsettings.json 文件中,它是以 JSON 格式记录的。下面是一个示例:
```json
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.AspNetCore.NewDb;Trusted_Connection=True;ConnectRetryCount=0"
},
"Logging": {
"LogLevel": {
"Default": "Warning"
}
}
}
```
通过使用 IConfiguration 接口来读取和访问配置信息。在一个 ASP.NET Core 应用程序中,可以通过依赖注入注入给控制器、服务等组件使用。以下是一个示例:
```csharp
using Microsoft.Extensions.Configuration;
public class HomeController : Controller
{
private readonly IConfiguration _configuration;
public HomeController(IConfiguration configuration)
{
_configuration = configuration;
}
public IActionResult Index()
{
var connectionString = _configuration.GetConnectionString("DefaultConnection");
var logLevel = _configuration["Logging:LogLevel:Default"];
// ...
}
}
```
在上面的示例中,我们注入了一个 IConfiguration 对象,并在 Index 方法中使用了它来获取连接字符串和日志级别。
三、配置的热更新
在一个应用程序运行时,如果需要修改配置信息,例如更改日志级别或数据库连接字符串,传统的做法是停止应用程序,修改配置文件,再重新启动应用程序。这种方式虽然可行,但有一些缺点:
1. 中断服务:在修改配置文件和重新启动应用程序之间,应用程序将中断服务;
2. 风险高:修改配置文件可能会导致一些潜在的风险,例如修改错误的连接字符串可能会导致应用程序无法连接数据库;
3. 不方便:修改配置文件需要手动操作,不方便。
为了解决这些问题,.NET Core 提供了配置的热更新功能,即在应用程序运行时修改配置信息而不需要重新启动应用程序,这种方式既安全又方便。
.NET Core 中的配置热更新有两种方式:
1. 监听文件:当配置文件发生改变时,重新加载配置信息;
2. 使用 IOptionsSnapshot<>:通过注入 IOptionsSnapshot<>,在需要获取配置信息时自动获取最新的配置信息。
以下是两种方式的示例:
1.监听文件
```csharp
using System.IO;
using Microsoft.Extensions.Configuration;
public static void Main(string[] args)
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); // 监听配置文件
var configuration = builder.Build();
var connectionString = configuration.GetConnectionString("DefaultConnection");
var logLevel = configuration["Logging:LogLevel:Default"];
Console.WriteLine($"connectionString: {connectionString}");
Console.WriteLine($"logLevel: {logLevel}");
// 等待文件修改
while (true)
{
Thread.Sleep(1000); // 1秒监测一次
if (File.Exists("appsettings.json"))
{
var lastModified = File.GetLastWriteTimeUtc("appsettings.json");
if (lastModified > configurationReloadTime) // 配置文件修改
{
configurationReloadTime = lastModified;
var newConnectionString = configuration.GetConnectionString("DefaultConnection");
var newLogLevel = configuration["Logging:LogLevel:Default"];
Console.WriteLine($"New configuration loaded. connectionString: {newConnectionString}, logLevel: {newLogLevel}");
}
}
}
}
```
在上面的示例中,我们使用了 SetBasePath 和 AddJsonFile 方法来加载 appsettings.json 文件,并设置 reloadOnChange 参数为 true,表示需要监听该文件的变化。当文件变化时,配置信息将会重新加载。
2.使用 IOptionsSnapshot<>
```csharp
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
public class HomeController : Controller
{
private readonly ConnectionStringsConfiguration _connectionStringsConfiguration;
private readonly LoggingConfiguration _loggingConfiguration;
public HomeController(IOptionsSnapshot IOptionsSnapshot { _connectionStringsConfiguration = connectionStringsConfiguration.Value; // 自动获取配置信息 _loggingConfiguration = loggingConfiguration.Value; } public IActionResult Index() { var connectionString = _connectionStringsConfiguration.DefaultConnection; var logLevel = _loggingConfiguration.LogLevel; // ... } } public class ConnectionStringsConfiguration { public string DefaultConnection { get; set; } } public class LoggingConfiguration { public string LogLevel { get; set; } } public void ConfigureServices(IServiceCollection services) { services.Configure services.Configure // ... } ``` 在上面的示例中,我们将 Configuration 对象中的 ConnectionStrings 和 Logging 配置节点绑定到 ConnectionStringsConfiguration 和 LoggingConfiguration 类中,并注册到 DI 中。然后,在 HomeController 中注入 IOptionsSnapshot 四、配置的框架设计 在 .NET Core 中,配置模块的实现可分为三层: 1. Configuration:表示配置模块中的核心接口,提供了访问配置信息的方法; 2. Configuration.Providers:表示配置的提供者,各种不同的配置信息都是由不同的提供者提供的; 3. Configuration.Binder:表示配置的绑定器,用于将配置信息绑定到 .NET 类型中。 总体框架如下图所示: ![image](https://user-images.githubusercontent.com/47147484/136487054-56b615a9-cebd-49c4-96a0-83097d265018.png) 1. IConfiguration IConfiguration 是配置模块中的核心接口,它定义了读取配置信息的方法。在使用 IConfiguration 接口时,可以使用以下方法: ```csharp IConfiguration configuration; // 获取连接字符串 var connectionString = configuration.GetConnectionString("DefaultConnection"); // 直接读取配置值 var logLevel = configuration["Logging:LogLevel:Default"]; ``` 2. IConfigurationBuilder IConfigurationBuilder 是用于构建 IConfiguration 对象的工厂方法,每当需要创建一个 IConfiguration 对象时,都需要使用 IConfigurationBuilder。在使用 IConfigurationBuilder 构建 IConfiguration 对象时,需要设置以下参数: 1. SetBasePath:设置配置文件的基础路劲,用于防止相对路径问题; 2. AddJsonFile:添加一个 JSON 配置文件; 3. AddInMemoryCollection:添加一个内存配置; 4. AddEnvironmentVariables:添加环境变量配置; 5. AddCommandLine:添加命令行参数配置。 以下是一个使用 IConfigurationBuilder 的例子: ```csharp var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true) .AddEnvironmentVariables(); var configuration = builder.Build(); ``` 在上面的示例中,我们通过 SetBasePath 方法设置 appsettings.json 文件的基础路径,然后通过 AddJsonFile 方法添加了两个 JSON 配置文件,后者根据不同的环境自动选择不同的配置文件。最后,通过 AddEnvironmentVariables 方法添加了环境变量配置。 3. IConfigurationProvider IConfigurationProvider 是配置的提供者接口,它负责提供配置信息。每当需要读取配置信息时,都需要通过 IConfigurationProvider 来读取配置信息。在使用 IConfigurationProvider 时,需要实现以下方法: 1. Load:用于加载配置信息; 2. GetReloadToken:用于获取监控配置变更的 ReloadToken 对象; 3. TryGet:用于从内存中获取配置值; 4. Set:用于将配置值存储到内存中。 以下是一个 IConfigurationProvider 接口的示例: ```csharp public interface IConfigurationProvider { void Load(); IChangeToken GetReloadToken(); IEnumerable bool TryGet(string key, out string value); void Set(string key, string value); } ``` 4. IConfigurationRoot IConfigurationRoot 是 IConfiguration 接口的默认实现,它实现了访问配置信息的方法。在 IConfigurationRoot 内部,维护了一个 IConfigurationProvider 的列表,在需要读取配置信息时,会遍历全部的 IConfigurationProvider,直到找到正确的配置值。以下是一个 IConfigurationRoot 的示例: ```csharp public class ConfigurationRoot : IConfigurationRoot { private readonly IEnumerable public ConfigurationRoot(IEnumerable { _providers = providers; } public IEnumerable public string this[string key] { get { foreach (var provider in Providers.Reverse()) { if (provider.TryGet(key, out var value)) { return value; } } return null; } set { foreach (var provider in Providers) { if (provider.TryGet(key, out var _)) { provider.Set(key, value); break; } } } } public IConfigurationSection GetSection(string key) { foreach (var provider in Providers.Reverse()) { var configurationSection = provider.GetSection(key); if (configurationSection.Exists()) { return configurationSection; } } return new ConfigurationSection(this, key); } public IEnumerable { return GetChildrenImplementation(null); } public IChangeToken GetReloadToken() { var changeTokenProviders = Providers.OfType if (changeTokenProviders.Any()) { return new CompositeChangeToken(changeTokenProviders.Select(p => p.GetReloadToken())); } return NullChangeToken.Singleton; } } ``` 在上面的代码中,我们实现了 IConfigurationRoot 接口,并且维护了一个 IConfigurationProvider 的列表。在获取配置信息时,会根据先后顺序依次调用 IConfigurationProvider 的 TryGet 方法。在获取子级配置时,会调用 IConfigurationProvider 的 GetSection 方法。在监视配置变更时,会调用 IReloadTokenProvider 接口的 GetReloadToken 方法。因此,当配置信息发生变更时,IConfigurationRoot 将会调用 IReloadTokenProvider 的 GetReloadToken 方法,并通过 ChangeToken 触发配置变更事件。 总的来说,.NET Core 的配置模块提供了一种集中化的方式来管理配置信息,并通过 IConfigurationProvider 和 IConfigurationRoot 实现了配置信息的读取、存储和监视等功能,而通过 IConfiguration 接口和 IConfigurationBuilder 工厂方法可以方便地使用和构建 IConfiguration 对象。配置的热更新功能则进一步提升了应用程序的可用性和灵活性。
如果你喜欢我们的文章,欢迎您分享或收藏为众码农的文章! 我们网站的目标是帮助每一个对编程和网站建设以及各类acg,galgame,SLG游戏感兴趣的人,无论他们的水平和经验如何。我们相信,只要有热情和毅力,任何人都可以成为一个优秀的程序员。欢迎你加入我们,开始你的美妙旅程!www.weizhongchou.cn
发表评论 取消回复