AspNetCore Options
前言
Options
1.基础知识
Options:
-
依赖项
Microsoft.Extensions.Options:选项的核心包,扩展IServiceCollection接口,只支持内存配置。
Microsoft.Extensions.Options.ConfigurationExtensions:配置文件的扩展,支持IConfiguration进行配置。
Microsoft.Extensions.DependencyInjection:选项必须配合容器使用
Microsoft.Extensions.Options.DataAnnotations:支持数据注解验证
-
核心接口
IOptions
:单实列,不支持命名。 IOptionsSnapshot
:每次实列化时更新选项,scope级别,支持命名配置 IOptionsMonitor
:监听配置更改并通知,Singleton级别,支持命名配置 IOptionsFactory
:管理Options实列,包括创建、配置、验证,支持命名配置 IConfigureOptions:保存配置选项时的委托,一个选项可以添加多个委托进行配置
IPostConfigureOptions:保存配置选项的委托,在IConfigureOptions委托执行之后执行
OptionsChangeTokenSource:用于监听IConfiguration的更改通知
IValidateOptions:配置选项之后的验证
2.基本使用
安装包:
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1" />
</ItemGroup>
代码:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
var services = new ServiceCollection();
// 第一种 通过委托来配置选项,同一个选项可以按顺序执行多个Configure。
services.Configure<TemCofig>(config =>
{
config.Field1 = "11111";
});
// 第二种 在所有的Configure执行之后配置选项。同一个选项可以按顺序执行多个PostConfigure。
services.PostConfigure<TemCofig>(config =>
{
config.Field2 = "22222";
});
// 第三种 AddOptions支持链式调用。本质还是执行Configure和PostConfigure
//services.AddOptions<TemCofig>()
// .Configure(config => { config.Field1 = "33333"; })
// .PostConfigure(postConfig => { postConfig.Field2 = "44444"; });
var sp = services.BuildServiceProvider();
//解析选项
var optionsFactory = sp.GetRequiredService<IOptionsFactory<TemCofig>>();
var options = sp.GetRequiredService<IOptions<TemCofig>>();
var optionsMonitor = sp.GetRequiredService<IOptionsMonitor<TemCofig>>();
var optionsSnapshot = sp.GetRequiredService<IOptionsSnapshot<TemCofig>>();
Console.WriteLine("Hello, World!");
Console.ReadKey();
public class TemCofig
{
public string Field1 { get; set; }
public string Field2 { get; set; }
}
3.源码
1.调用Configure会向容器注册一个IConfigureOptions
2.调用PostConfigure会向容器注册一个IPostConfigureOptions
3.Configure或者PostConfigure都会执行AddOptions();
4.AddOptions();会尝试向容器注册IOptions、IOptionsSnapshot、IOptionsMonitor、IOptionsFactory、IOptionsMonitorCache
5.IOptionsFactory内部实现逻辑是,首先加载所有注册的IConfigureOptions、IPostConfigureOptions、IValidateOptions服务实列Create方法首先通过反射调用选项的无参数构造器来实列化选项。然后通过选项实列来执行所有IConfigureOptions中的委托,然后在执行所有IPostConfigureOptions中委托,然后在执行IValidateOptions
6.IOptions实列依赖IOptionsFactory创建一个名为Options.DefaultName的选项,由于是单实列的,只能执行一次委托来计算选项的值。
7.IOptionsSnapshot依赖IOptionsFactory创建一个名为Options.DefaultName的选项Value,并支持命名Get方法获取选项(内置缓存机制)因为是Scoped所以每次获取新的选项实列都会执行委托来重新计算选项值。
8.IOptionsMonitor依赖IOptionsFactory提供支持命名的Get方法获取选项,虽然IOptionsMonitor是单实列的并且会监听配置更改,可以通过注册OnChange来执行更改之后的逻辑来更新选项。
4.监听更改
IOptions
IOptionsSnapshot
IOptionsMonitor
安装包:
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" />
</ItemGroup>
添加配置文件:
{
"TmpConfig": {
"Field1": "123",
"Field2": "456"
}
}
代码:
reloadOnChange必须设置true
reloadOnChange:如果文件更改,是否应该重新加载配置。
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
var configuration = new ConfigurationManager();
configuration.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("TmpConfig.json", optional: false, reloadOnChange: true);
var services = new ServiceCollection();
services.Configure<TmpConfig>(configuration.GetSection("TmpConfig"));
var container = services.BuildServiceProvider();
while (true)
{
Thread.Sleep(2000);
using (var scope = container.CreateScope())
{
var o1 = scope.ServiceProvider.GetRequiredService<IOptions<TmpConfig>>();
var o2 = scope.ServiceProvider.GetRequiredService<IOptionsSnapshot<TmpConfig>>();
var o3 = scope.ServiceProvider.GetRequiredService<IOptionsMonitor<TmpConfig>>();
Console.WriteLine("==================");
Console.WriteLine($"IOptions:{o1.Value.Field1}---{o1.Value.Field2}");
// IOptionsSnapshot可以更改文件
Console.WriteLine($"IOptionsSnapshot:{o2.Value.Field1}---{o2.Value.Field2}");
Console.WriteLine($"IOptionsMonitor:{o3.CurrentValue.Field1}---{o3.CurrentValue.Field2}");
// IOptionsMonitor可执行更改时发生事件
o3.OnChange(o =>
{
Console.WriteLine("选项发生更改了");
});
}
}
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
Console.ReadKey();
public class TmpConfig
{
public string Field1 { get; set; }
public string Field2 { get; set; }
}
5.命名选项
安装包:
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1" />
</ItemGroup>
Options.DefaultName默认string.Empty
代码:
通过Get方法获取指定名称配置
6.高级配置
安装包:
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1" />
</ItemGroup>
6.1DI配置
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.VisualBasic;
using System.Runtime.Intrinsics.Arm;
var services = new ServiceCollection();
// 注册DI服务,只能是单实例
services.AddSingleton<TmpConfigConfigration>();
services.AddOptions<TmpConfig>()
//.Configure(c => { c.Field1 = "33333"; c.Field2 = "44444"; })
.PostConfigure<TmpConfigConfigration>((options, dep) =>
{
dep.Configure(options);
});
var container = services.BuildServiceProvider();
var options = container.GetRequiredService<IOptions<TmpConfig>>();
Console.WriteLine("Hello, World!");
Console.ReadKey();
public class TmpConfig
{
public string Field1 { get; set; }
public string Field2 { get; set; }
}
internal class TmpConfigConfigration
{
public void Configure(TmpConfig config)
{
config.Field1 = "11111";
config.Field2 = "22222";
}
}
6.2IConfigureOptions
var services = new ServiceCollection();
services.AddOptions();
services.AddSingleton<IConfigureOptions<TmpConfig>, TmpConfigConfigureOptions>();
var container = services.BuildServiceProvider();
var tmpConfigOptions = container.GetRequiredService<IOptions<TmpConfig>>();
public class TmpConfig
{
public string Field1 { get; set; }
public string Field2 { get; set; }
}
internal class TmpConfigConfigureOptions : IConfigureOptions<TmpConfig>
{
public void Configure(TmpConfig tmpConfig)
{
tmpConfig.Field1 = "11111";
tmpConfig.Field2 = "22222";
}
}
6.3IPostConfigureOptions
var services = new ServiceCollection();
services.AddOptions();
services.AddSingleton<IPostConfigureOptions<TmpConfig>, TmpConfigPostConfigureOptions>();
var container = services.BuildServiceProvider();
var tmpConfigOptions = container.GetRequiredService<IOptions<TmpConfig>>();
public class TmpConfig
{
public string Field1 { get; set; }
public string Field2 { get; set; }
}
internal class TmpConfigPostConfigureOptions : IPostConfigureOptions<TmpConfig>
{
public void PostConfigure(string? name, TmpConfig tmpConfig)
{
tmpConfig.Field1 = "11111";
tmpConfig.Field2 = "22222";
}
}
7.验证选项
安装包
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="7.0.0" />
</ItemGroup>
7.1委托验证
var services = new ServiceCollection();
//不是把选项注入到容器,只是注入了该选项的委托
services.Configure<TmpConfig>(c => { c.Field1 = ""; c.Field2 = "22222"; });
//得到OptionsBuilder,进行验证,本质还是执行Configure(可以执行无数多个Configre)
services.AddOptions<TmpConfig>()
.Validate(optison =>
{
if (optison.Field1 == "")
{
return false;
}
if (optison.Field2 == "")
{
return false;
}
return true;
}, "TmpConfig配置有误,请检查!!!");
var container = services.BuildServiceProvider();
var options = container.GetRequiredService<IOptions<TmpConfig>>();
public class TmpConfig
{
public string Field1 { get; set; }
public string Field2 { get; set; }
}
7.2 注解验证
var services = new ServiceCollection();
//不是把选项注入到容器,只是注入了该选项的委托
services.Configure<TmpConfig>(c => { c.Field1 = " "; c.Field2 = " "; });
//得到OptionsBuilder,进行验证,本质还是执行Configure(可以执行无数多个Configre)
services.AddOptions<TmpConfig>()
.ValidateDataAnnotations();
var container = services.BuildServiceProvider();
var options = container.GetRequiredService<IOptions<TmpConfig>>();
Console.WriteLine(options.Value.Field1);
public class TmpConfig
{
[RegularExpression(@"/^.+$/", ErrorMessage = "不能为空")]
public string Field1 { get; set; }
[RegularExpression(@"/^.+$/", ErrorMessage = "不能为空")]
public string Field2 { get; set; }
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!