• 看看.NET Core几个Options的简单使用


    前言

    配置,对我们的程序来说是十分重要的一部分。或多或少都会写一部分内容到配置文件中去。

    由其是在配置中心(Apollo等)做起来之前,配置文件一定会是我们的首选。

    在.NET Core中,习惯的是用json文件当配置文件。读取的方法是不少,这里主要介绍的是用基于Options的方法来读,可以认为这是一种强类型的形式。

    本文会介绍一些常见的用法,简单的单元测试示例,如果想探讨内部实现,请移步至雨夜朦胧的博客

    先来看看IOptions。

    IOptions

    先写好配置文件

    {
        "Demo": {
            "Age": 18,
            "Name": "catcher"
        },
        //others ... 
    }
    

    然后定义对应的实体类

    public class DemoOptions
    {
        public int Age { get; set; }
    
        public string Name { get; set; }
    }
    

    然后只需要在ConfigureServices方法添加一行代码就可以正常使用了。

    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<DemoOptions>(Configuration.GetSection("Demo"));
        
        //others..
    }
    

    最后就是在想要读配置内容的地方使用IOptions去注入就好了。

    private readonly DemoOptions _normal;
    
    public ValuesController(IOptions<DemoOptions> normalAcc)
    {
        this._normal = normalAcc.Value;
    }
    
    // GET api/values
    [HttpGet]
    public string Get()
    {
        var age = $"normal-[{_normal.Age}];";
        var name = $"normal-[{_normal.Name}];";
    
        return $"age:{age} 
    name:{name}";
    }
    

    这个时候的结果,就会大致如下了:

    这个时候可能会冒出这样的一个想法,如果某天,要修改某个配置项的值,它能及时生效吗?

    口说无凭,来个动图见证一下。

    事实证明,使用IOptions的时候,修改配置文件的值,并不会立刻生效!!

    既然IOptions不行,那么我们就换一个!

    下面来看看IOptionsSnapshot。

    IOptionsSnapshot

    对于Options家族,在Startup注册的时候都是一个样的,区别在于使用它们的时候。

    private readonly DemoOptions _normal;
    private readonly DemoOptions _snapshot;
    
    public ValuesController(IOptions<DemoOptions> normalAcc,
            IOptionsSnapshot<DemoOptions> snapshotAcc)
    {
        this._normal = normalAcc.Value;
        this._snapshot = snapshotAcc.Value;
    }
    
    // GET api/values
    [HttpGet]
    public string Get()
    {
        var age = $"normal-[{_normal.Age}];snapshot-[{_snapshot.Age}];";
        var name = $"normal-[{_normal.Name}];snapshot-[{_snapshot.Name}];";
    
        return $"age:{age} 
    name:{name}";
    }
    

    这个时候修改配置项的值之后,就会立马更新了。

    本质上,IOptions和IOptionsSnapshot是一样的,只是他们注册的生命周期不一样,从而就有不同的表现结果。

    当然,还有一个更强大的Options的存在,IOptionsMonitor。

    它的用法和IOptionsSnapshot没有区别,不同的时,它多了一个配置文件发生改变之后事件处理。

    下面来看看。

    IOptionsMonitor

    private readonly DemoOptions _normal;
    private readonly DemoOptions _snapshot;
    private readonly DemoOptions _monitor;
    
    public ValuesController(IOptions<DemoOptions> normalAcc, IOptionsSnapshot<DemoOptions> snapshotAcc, IOptionsMonitor<DemoOptions> monitorAcc)
    {
        this._normal = normalAcc.Value;
        this._snapshot = snapshotAcc.Value;
        this._monitor = monitorAcc.CurrentValue;
        monitorAcc.OnChange(ChangeListener);
    }
    
    private void ChangeListener(DemoOptions options, string name)
    {
        Console.WriteLine(name);
    }
    
    // GET api/values
    [HttpGet]
    public string Get()
    {
        var age = $"normal-[{_normal.Age}];snapshot-[{_snapshot.Age}];monitor-[{_monitor.Age}];";
        var name = $"normal-[{_normal.Name}];snapshot-[{_snapshot.Name}];monitor-[{_monitor.Name}];";
    
        return $"age:{age} 
    name:{name}";
    }
    

    效果和上面一样的,不同的是,当保存appsettings.json的时候,会触发一次ChangeListener

    虽说Snapshot和Monitor可以让我们及时获取到最新的配置项。

    但是我们也可以通过PostConfigurePostConfigureAll来进行调整。

    PostConfigure/PostConfigureAll

    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<DemoOptions>(Configuration.GetSection("Demo"));
    
        services.PostConfigureAll<DemoOptions>(x =>
        {
            x.Age = 100;
        });
    
        services.AddMvc();
    }
    

    如果我们的代码是这样写的,那么,最终的结果就会是,无论我们怎么修改配置文件,最终展示的Age会一直是100。

    大家也可以思考一下这个可以用在什么场景。

    随便给大家看一段Steeltoe服务发现客户端的代码

    private static void AddDiscoveryServices(IServiceCollection services, IConfiguration config, IDiscoveryLifecycle lifecycle)
    {
        var clientConfigsection = config.GetSection(EUREKA_PREFIX);
        int childCount = clientConfigsection.GetChildren().Count();
        if (childCount > 0)
        {
            var clientSection = config.GetSection(EurekaClientOptions.EUREKA_CLIENT_CONFIGURATION_PREFIX);
            services.Configure<EurekaClientOptions>(clientSection);
    
            var instSection = config.GetSection(EurekaInstanceOptions.EUREKA_INSTANCE_CONFIGURATION_PREFIX);
            services.Configure<EurekaInstanceOptions>(instSection);
            services.PostConfigure<EurekaInstanceOptions>((options) =>
            {
                EurekaPostConfigurer.UpdateConfiguration(config, options);
            });
            AddEurekaServices(services, lifecycle);
        }
        else
        {
            throw new ArgumentException("Discovery client type UNKNOWN, check configuration");
        }
    }
    

    最后就是单元测试遇到Options要怎么处理的问题了。

    单元测试

    单元测试,这里用了NSubstitute来作示例。

    先简单定义一些类

    public class MyClass
    {
        private readonly MyOptions _options;
    
        public MyClass(IOptions<MyOptions> optionsAcc)
        {
            this._options = optionsAcc.Value;
        }
    
        public string Greet()
        {
            return $"Hello,{_options.Name}";
        }
    }
    
    public class MyOptions
    {
        public string Name { get; set; }        
    }
    

    编写测试类

    public class MyClassTest
    {
        private readonly MyClass myClass;
    
        public MyClassTest()
        {
            var options = new MyOptions {  Name = "catcher"};
    
            var fake = Substitute.For<IOptions<MyOptions>>();
    
            fake.Value.Returns(options);
    
            myClass = new MyClass(fake);
        }
    
        [Fact]
        public void GreetTest()
        {
            var res = myClass.Greet();
    
            Assert.Equal("Hello,catcher", res);
        }
    }
    

    重点在于fake了一下Options(这里只以IOptions为例),然后是告诉测试,如果有用到Value属性的时候,就用返回定义好的Options。

    也是比较简单的做法,测试的结果自然也是符合预期的。

    总结

    这几个Options使用起来还是比较顺手的,至少何时采用那种Options,就得根据场景来定了。

  • 相关阅读:
    Centos搭建PHP5.3.8+Nginx1.0.9+Mysql5.5.17
    初识Mongodb总结
    初识Mongodb之[CURD]PHP版
    Centos搭建Samba
    PHP图像处理(二) GraphicsMagick 安装扩展及使用方法
    Vcastr3.0开源在线flv播放器
    自动更新@version svn版本号信息
    Centos安装Memcache
    MVC演化
    JAVA与.NET的相互调用——TCP/IP相互调用基本架构
  • 原文地址:https://www.cnblogs.com/catcher1994/p/9190978.html
Copyright © 2020-2023  润新知