站点图标 神之眼的博客

在 .NET 10 LTS 中,IOptions<TOptions>和 IOptionsSnapshot<TOptions>的主要区别

當前文章的短網址連結為: https://unos.top/sp3v

两者的主要区别体现在生命周期、配置重新加载和对配置变更的感知上。

特性IOptions<TOptions>IOptionsSnapshot<TOptions>
生命周期单例​作用域​
配置重新加载不支持。在应用启动时加载一次,之后不会更改。支持。在每个作用域内,它会与当前配置源(如 appsettings.json)同步,读取最新值。
变更感知不感知配置文件的更改。感知更改。每次从新的作用域解析时,都会提供最新的配置数据。
主要用途用于在应用整个生命周期内不会改变的配置。用于需要响应运行时配置更改的场景(如云环境、功能开关)。
性能最佳。只计算一次。每次创建新作用域时有微小开销,因为它会重新绑定配置。
错误处理在应用启动时验证并绑定配置,如果配置错误,应用会立即失败。在每次解析时验证,错误可能延迟到请求处理时才暴露。

详细解释与示例

假设你有以下配置类:

public class MySettings
{
    public string ApiEndpoint { get; set; }
    public int Timeout { get; set; }
}
C#

在 Startup/Program.cs中配置:

builder.Services.Configure<MySettings>(builder.Configuration.GetSection("MySettings"));
C#

1. 使用 IOptions<>(单例,不可变)

public class MyService
{
    private readonly MySettings _settings;
    
    // 注入 IOptions<MySettings>,它是一个单例
    public MyService(IOptions<MySettings> options)
    {
        // 只在构造函数中读取一次值
        _settings = options.Value; // 此值在整个应用生命周期内保持不变
    }
}
C#

行为:即使你在应用运行时修改了 appsettings.json文件,_settings中的值也不会更新。它持有的是应用启动时的快照。

2. 使用 IOptionsSnapshot<>(作用域,可重新加载)

public class MyController : ControllerBase
{
    private readonly MySettings _settings;
    
    // 注入 IOptionsSnapshot<MySettings>,它的生命周期是“作用域”
    public MyController(IOptionsSnapshot<MySettings> optionsSnapshot)
    {
        // 每次构造控制器(即每个HTTP请求)时,都会读取最新的配置
        _settings = optionsSnapshot.Value; // 此值会反映最新的配置文件内容
    }
}
C#

行为:每个传入的 HTTP 请求(即每个作用域),optionsSnapshot.Value都会从当前的配置源重新加载并绑定数据。如果你在请求间隔中修改了配置文件,下一个请求将获取到新值。

如何选择?

另外一个重要的相关接口:IOptionsMonitor<>

除了上述两者,还有一个更高级的接口 IOptionsMonitor<>

public class MyBackgroundService : BackgroundService
{
    private readonly IOptionsMonitor<MySettings> _monitor;
    
    public MyBackgroundService(IOptionsMonitor<MySettings> monitor)
    {
        _monitor = monitor;
        
        // 注册一个当配置改变时的回调函数
        _monitor.OnChange(newSettings => 
        {
            Console.WriteLine($"配置已更新!新端点: {newSettings.ApiEndpoint}");
        });
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            // 始终从单例的 Monitor 中获取当前值
            var currentSettings = _monitor.CurrentValue;
            
            // ... 使用 currentSettings 工作
            await Task.Delay(5000, stoppingToken);
        }
    }
}
C#

总结

接口生命周期配置更新典型的使用场景
IOptions<>​单例启动后不变的全局配置
IOptionsSnapshot<>​作用域是(按作用域)Web API/控制器中,需要每个请求都读取最新配置
IOptionsMonitor<>​单例是(实时监听)后台服务、单例服务,需要监听配置变更事件

退出移动版