You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让IOptionsSnapshot.Get()遇无效名称时抛出异常?

当传入未注册的名称时,IOptionsSnapshot.Get(string? name)会返回一个“默认”实例
当前应用仅注册了一个名为GovernanceServiceOptions.UnassignedApplications的命名选项。调用IOptionsSnapshot<T>.Get()传入无效名称时,方法会返回默认实例(比如new GovernanceServiceOptions())。希望让该方法在传入无效名称时抛出异常,该如何实现?

解决方案

要实现这个需求,我们可以通过包装IOptionsSnapshot<T>并跟踪已注册的选项名称,在调用Get方法时校验名称合法性,未注册则抛出异常。

1. 实现已注册选项名称跟踪器

创建一个单例服务,用于记录所有注册过的命名选项名称:

public class RegisteredOptionsTracker
{
    private readonly Dictionary<Type, HashSet<string>> _registeredNames = new();

    public void RegisterOptionName<T>(string name)
    {
        var type = typeof(T);
        if (!_registeredNames.ContainsKey(type))
        {
            _registeredNames[type] = new HashSet<string>();
        }
        _registeredNames[type].Add(name ?? Options.DefaultName);
    }

    public bool IsOptionNameRegistered<T>(string? name)
    {
        var type = typeof(T);
        if (!_registeredNames.TryGetValue(type, out var names))
        {
            return false;
        }
        return names.Contains(name ?? Options.DefaultName);
    }
}

2. 扩展选项注册逻辑,自动记录名称

扩展IServiceCollection,让注册命名选项时自动将名称存入跟踪器:

public static class OptionsRegistrationExtensions
{
    public static OptionsBuilder<TOptions> AddNamedOptions<TOptions>(this IServiceCollection services, string? name)
        where TOptions : class
    {
        services.AddSingleton<RegisteredOptionsTracker>();
        
        var tracker = services.BuildServiceProvider().GetRequiredService<RegisteredOptionsTracker>();
        tracker.RegisterOptionName<TOptions>(name);
        
        return services.AddOptions<TOptions>(name);
    }
}

修改原有选项注册代码,使用这个扩展方法:

services.AddNamedOptions<GovernanceServiceOptions>(GovernanceServiceOptions.UnassignedApplications)
    .BindConfiguration(GovernanceServiceOptions.UnassignedApplications)
    .ValidateDataAnnotations()
    .ValidateOnStart();

3. 包装IOptionsSnapshot实现校验逻辑

创建包装类,拦截Get方法做合法性校验:

public class ValidatingOptionsSnapshot<TOptions> : IOptionsSnapshot<TOptions>
    where TOptions : class
{
    private readonly IOptionsSnapshot<TOptions> _innerSnapshot;
    private readonly RegisteredOptionsTracker _tracker;

    public ValidatingOptionsSnapshot(IOptionsSnapshot<TOptions> innerSnapshot, RegisteredOptionsTracker tracker)
    {
        _innerSnapshot = innerSnapshot;
        _tracker = tracker;
    }

    public TOptions Value => _innerSnapshot.Value;

    public TOptions Get(string? name)
    {
        var targetName = name ?? Options.DefaultName;
        if (!_tracker.IsOptionNameRegistered<TOptions>(name))
        {
            throw new InvalidOperationException($"命名选项 '{targetName}' 未注册,请检查选项名称是否正确。");
        }
        return _innerSnapshot.Get(name);
    }
}

4. 替换DI容器中的IOptionsSnapshot

在服务注册中替换默认实现为我们的校验包装类:

public static class ValidatingOptionsExtensions
{
    public static IServiceCollection AddValidatingOptionsSnapshot<TOptions>(this IServiceCollection services)
        where TOptions : class
    {
        services.AddScoped<IOptionsSnapshot<TOptions>>(sp =>
        {
            var innerSnapshot = sp.GetRequiredService<IOptionsSnapshot<TOptions>>();
            var tracker = sp.GetRequiredService<RegisteredOptionsTracker>();
            return new ValidatingOptionsSnapshot<TOptions>(innerSnapshot, tracker);
        });
        return services;
    }
}

在Program.cs/Startup中添加注册:

services.AddValidatingOptionsSnapshot<GovernanceServiceOptions>();

效果验证

现在调用governanceServiceOptions.Get("无效名称")会直接抛出InvalidOperationException;传入已注册的名称则正常返回配置好的选项。

内容的提问来源于stack exchange,提问作者Shuzheng

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 19:04:51