写点什么

关于 ASP.NET Core 中的选项模式

作者:雄鹿 @
  • 2024-03-28
    广东
  • 本文字数:3390 字

    阅读完需:约 11 分钟

关于 ASP.NET Core 中的选项模式

在 ASP.NET Core 中,选项模式可以简化配置的处理

选项模式使得代码更加整洁,可以避免在代码中进行大量的字符串解析和转换操作

先决条件

.NET 8 SDK

介绍

选项模式就是一种采用依赖注入来提供配置数据的编程方式

并不是直接利用依赖注入来提供配置对象,而是通过依赖注入提供选项对象来提供配置对象

选项对象有三种

  • IOptions<> - 基础选项对象

  • IOptionsSnapshot<> - 快照选项对象

  • IOptionsMonitor<> - 可监控选项对象

代码准备

首先需要引入相关的 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);        }    }
复制代码


发布于: 刚刚阅读数: 4
用户头像

雄鹿 @

关注

心像开满花的树。 2019-01-04 加入

一名全栈开发工程师,热爱编程、徒步、登山和摄影,对新技术充满好奇心,专注于使用ASP.NET Core和Angular进行Web应用的开发。

评论

发布
暂无评论
关于 ASP.NET Core 中的选项模式_ASP.NET Core_雄鹿 @_InfoQ写作社区