.NetCore选项配置中的IOptions,IOptionsM

2020-10-23  本文已影响0人  AlexWillBeGood

ASP.NET Core中可以使用Options模式,从而能够使用强类型的类来表达配置项,这也是实现配置的最佳实践。
对外提供的配置项接口有IOptions<TOptions>,IOptionsMonitor<TOptions>,IOptionsSnapshot<TOptions>,这三种适用于不同的场景:

举个例子:

创建一个Console程序,并添加一个配置文件appsettings.json

{
  "TestOptions": {
    "Name": "0"
  }
}

并且引入以下扩展包:


image.png

Main函数中写入

static void Main(string[] args)
        {
            IConfigurationRoot root = new ConfigurationBuilder()
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .Build();

            var services = new ServiceCollection();
            services.AddOptions();
            services.Configure<TestOptions>(root.GetSection("TestOptions"));

            var provider = services.BuildServiceProvider();

            Console.WriteLine("原始值:");
            ShowOptions(provider);

            Console.WriteLine("第一次代码修改:");
            //第一次修改(代码修改)
            Change(provider);
            ShowOptions(provider);

            Console.WriteLine("第二次手动修改:");
            //第二次修改(手动修改)
            Console.WriteLine("开始修改配置文件");
            Console.ReadLine();
            ShowOptions(provider);

            Console.ReadKey();
        }

/// <summary>
        /// 显示所有的选项
        /// </summary>
        static void ShowOptions(IServiceProvider serviceProvider)
        {
            using (var scope = serviceProvider.CreateScope())
            {
                var sp = scope.ServiceProvider;
                var options = sp.GetRequiredService<IOptions<TestOptions>>();
                var monitor = sp.GetRequiredService<IOptionsMonitor<TestOptions>>();
                var snapshot = sp.GetRequiredService<IOptionsSnapshot<TestOptions>>();

                Console.WriteLine($"options:{options.Value.name}");
                Console.WriteLine($"monitor:{monitor.CurrentValue.name}");
                Console.WriteLine($"snapshot:{snapshot.Value.name}");
            }
        }
        /// <summary>
        /// 手动去改变
        /// </summary>
        static void Change(IServiceProvider serviceProvider)
        {
            using (var scope = serviceProvider.CreateScope())
            {
                var sp = scope.ServiceProvider;
                var options = sp.GetRequiredService<IOptions<TestOptions>>();
                var monitor = sp.GetRequiredService<IOptionsMonitor<TestOptions>>();
                var snapshot = sp.GetRequiredService<IOptionsSnapshot<TestOptions>>();

                options.Value.name = "1";
                monitor.CurrentValue.name = "1";
                snapshot.Value.name = "1";
            }
        }
        public class TestOptions
        {
            /// <summary>
            /// 名称
            /// </summary>
            public string name { get; set; }
        }

其实主要就是做了三步:

最终的结果是:


image.png

分析得出:

可以注意到这句代码实际上就是注册这些接口

var services = new ServiceCollection();
services.AddOptions();

查看源码:

public static IServiceCollection AddOptions(this IServiceCollection services)
{
    if (services == null)
    {
        throw new ArgumentNullException(nameof(services));
    }

    services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(OptionsManager<>)));
    services.TryAdd(ServiceDescriptor.Scoped(typeof(IOptionsSnapshot<>), typeof(OptionsManager<>)));
    services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitor<>), typeof(OptionsMonitor<>)));
    services.TryAdd(ServiceDescriptor.Transient(typeof(IOptionsFactory<>), typeof(OptionsFactory<>)));
    services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitorCache<>), typeof(OptionsCache<>)));
    return services;
}

可以看到IOptionsSnapshot注册生命周期是Scoped,而IOptions,IOptionsMonitor注册的生命周期是Singleton,这样就造成了代码修改配置,在不同的子容器中,IOptionsSnapshot获取到的服务都是互不影响的,因此在例子中没有改变,同时因为每一次创建都重新去读当前配置文件(snapshot),因为达到更新的目的。

至于IOptionsMonitor单例能够热更新配置,可以查看其实现代码:

public OptionsMonitor(IOptionsFactory<TOptions> factory, IEnumerable<IOptionsChangeTokenSource<TOptions>> sources, IOptionsMonitorCache<TOptions> cache)
{
    _factory = factory;
    _sources = sources;
    _cache = cache;

    foreach (var source in _sources)
    {
        var registration = ChangeToken.OnChange(
                () => source.GetChangeToken(),
                (name) => InvokeChanged(name),
                source.Name);

        _registrations.Add(registration);
    }
}

IOptionsChangeTokenSource<TOptions>使得其能够热更新。

三种类型使用顺序

上一篇 下一篇

猜你喜欢

热点阅读