在 ASP.NET Core 中,选项模式可以简化配置的处理
选项模式使得代码更加整洁,可以避免在代码中进行大量的字符串解析和转换操作
先决条件
.NET 8 SDK
介绍
选项模式就是一种采用依赖注入来提供配置数据的编程方式
并不是直接利用依赖注入来提供配置对象,而是通过依赖注入提供选项对象来提供配置对象
选项对象有三种
代码准备
首先需要引入相关的 NuGet 包
> # 输入命令从 NuGet 安装, 我用的NuGet包的版本为 3.1.10
> dotnet add package Microsoft.Extensions.Configuration.CommandLine
> dotnet add package Microsoft.Extensions.Configuration.Json
> dotnet add package Microsoft.Extensions.DependencyInjection
> # 选项模式的配置扩展包
> dotnet add package Microsoft.Extensions.Options.ConfigurationExtensions
复制代码
需要准备的 Class
public class Root
{
public Node FirstNode { get; set; }
public Node SecondNode { get; set; }
}
public class Node
{
public int Id { get; set; }
public string Name { get; set; }
}
复制代码
还有一个名为 appsettings.json 的 Json 文件
{
"FirstNode": { "Id": 1, "Name": "First" },
"SecondNode": { "Id": 2, "Name": "Second" }
}
复制代码
关于 IOptions<> 的使用
IOptions<> 生命周期为 Singleton,没有数据热更新,读取的值永远不会变
// 根据JSON配置文件构建一个配置对象
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
var services = new ServiceCollection()
// 添加使用选项所需的服务
.AddOptions()
// 将配置对象和选项对象做一个映射
.Configure<Root>(configuration);
var serviceProvider = services.BuildServiceProvider();
IOptions<Root> options = serviceProvider.GetRequiredService<IOptions<Root>>();
Root root = options.Value;
复制代码
以上代码中,首先使用 JSON 配置文件构建一个配置对象
然后使用 AddOptions() 将选项服务注册到服务注册对象,并使用 Configure<Root>(configuration) 将配置对象和选项对象做一个映射
后面是使用 BuildServiceProvider() 建构服务提供对象(容器),然后从容器中获取对象,最后通过 IOption<>.Value 拿到配置对象
关于 IOptionsSnapshot<> 的使用
IOptionsSnapshot<> 是快照选项对象,生命周期为 Scoped,有数据热更新功能,
在设置配置文件监控后,新的子容器获取的选项对象的配置会随着源文件的更改而更改
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", false, true)
.Build();
// 两套配置,相同的类型 AppConfigDemo
var serviceProvider = new ServiceCollection()
.AddOptions()
// 给注册的选项命名,并传入到绑定的节点
.Configure<Root>(configuration)
.Configure<Node>("FirstNode", configuration.GetSection("FirstNode"))
.Configure<Node>("SecondNode", configuration.GetSection("SecondNode"))
.BuildServiceProvider();
// 没有命名的还是通过 Value 获取值
Root root = serviceProvider.GetRequiredService<IOptionsSnapshot<Root>>().Value;
using (IServiceScope scope = serviceProvider.CreateScope())
{
IServiceProvider child = scope.ServiceProvider;
// 这时获取的不是 IOptions了,而是 IOptionsSnapshot 快照选项
IOptionsSnapshot<Node> options = child.GetRequiredService<IOptionsSnapshot<Node>>();
// 通过Get方法或者指定名称的配置
Node firstNode = options.Get("FirstNode");
Node secondNode = options.Get("SecondNode");
}
Console.ReadLine();
using (IServiceScope scope = serviceProvider.CreateScope())
{
IServiceProvider child = scope.ServiceProvider;
// 这时获取的不是 IOptions了,而是 IOptionsSnapshot 快照选项
IOptionsSnapshot<Node> options = child.GetRequiredService<IOptionsSnapshot<Node>>();
// 通过Get方法或者指定名称的配置
Node firstNode = options.Get("FirstNode");
Node secondNode = options.Get("SecondNode");
}
复制代码
在使用 IOptionsSnapshot<> 时,有些内容和 IOptions<> 是一样的,
只不过获取的时候从 GetRequiredService<IOptions<T>> 变成了 GetRequiredService<IOptionsSnapshot<T>>()
并且可以给注册的选项命名,并传入要绑定的节点,例如这里将 FirstNode 和 SecondNode 都绑定到了 Node 这个配置对象
在获取对象时,就变成了直接通过 Get("") 方法传入名称进行获取值
在上面示例中,两个子容器之前用 Console.ReadLine() 作为等待,这时去修改 appsettings.json 文件后再往后执行,会发现新的子容器获取的值随着更改而改变
关于 IOptionsMonitor<>的使用
IOptionsMonitor<> 是可监控的选项对象,生命周期为 Singleton,有数据热更新功能
在设置配置文件监控后,新获取的选项对象配置会随着源文件更改而更改,并可以设置配置更改后的回调
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", false, true)
.Build();
var serviceProvider = new ServiceCollection()
.AddOptions()
.Configure<Root>(configuration)
// 带监控的选项也具有给选项命名
.Configure<Node>("FirstNode", configuration.GetSection("FirstNode"))
.Configure<Node>("SecondNode", configuration.GetSection("SecondNode"))
.BuildServiceProvider();
var rootOptions = serviceProvider.GetRequiredService<IOptionsMonitor<Root>>();
var root = rootOptions.CurrentValue;
var nodeOptions = serviceProvider.GetRequiredService<IOptionsMonitor<Node>>();
var firstNode = nodeOptions.Get("FirstNode");
var secondNode = nodeOptions.Get("SecondNode");
// 发生变化以后的回调,可以拿到所变化配置节的名称configName
nodeOptions.OnChange((appConfig, configName) =>
{
Console.WriteLine($"Name:{appConfig.Id}");
Console.WriteLine($"Name:{appConfig.Name}");
Console.WriteLine($"Name:{appConfig.Id}");
Console.WriteLine($"Name:{appConfig.Name}");
});
Console.Read();
复制代码
IOptionsMonitor<> 的内容基本上和 IOptionsSnapshot<> 差不多
主要增加了 OnChange 方法来设置配置更改以后的回调,还有就是生命周期的不同
验证选项参数的合法性
在这里我们通过命令行映射的配置通过命令行传入参数,并手动赋值参数给选项对象,
然后通过 Validate 判断传入的参数是否满足要求,并设置不满足要求时的错误提示
如满足,从容器中获取选项对象时将能成功运行,不能满足要求则报错,并将我们设置的提示显示出来
static void Main(string[] args)
{
var mapping = new Dictionary<string, string>() { ["-n"] = "Name", };
var config = new ConfigurationBuilder()
.AddCommandLine(args, mapping)
.Build();
var services = new ServiceCollection();
services.AddOptions<Node>()
.Configure(options => options.Name = config["Name"] ?? "")
.Validate(demo => demo.Name.EndsWith("Node"), "Name参数必须以Node结尾");
try
{
var options = services.BuildServiceProvider()
.GetRequiredService<IOptions<Node>>().Value;
Console.WriteLine(options.Name);
}
catch (OptionsValidationException ex)
{
Console.WriteLine(ex.Message);
}
}
复制代码
评论