.netcore入门30:.net core源码分析之选项模型(IOption)

环境:

  • window 10
  • .netcore 3.1
  • vs2019 16.5.5
  • dnSpy v6.1.4 (.NET Core)

一、选项模型概念

关于IOption

IOption被称为选项模型,它是在.netcore中引入的。它的作用是给我们的应用程序提供配置,这个配置就是一个个的对象模型。

关于“依赖注入”和“选项模型”:

“选项模型”本身是构筑在“依赖注入”框架上的,所以我们要想使用“选项模型”就必须先引入“依赖注入”框架。

关于“配置”和“选项模型”:

IConfigurationIOption在.netcore中被称为配置选项,前者表示配置,后者表示选项模型。它们二者在.netcore中是一对基石。关于IConfiguration的分析请查看上一篇文章:.netcore入门25:.net core源码分析之配置模块(IConfiguration)
虽然它们二者的关系紧密,而且选项模型的数据来源大多来自配置,但它们二者之间并没有直接的依赖关系,也就是说它们二者任何一个都可以单独拿出来使用。后面讲解的时候会先独立分析选项模型,然后再分析它们二者的结合。

关于选项模型的包:

  • 选项模型本身就一个nuget包:Microsoft.Extensions.Options
  • 选项模型是运行在依赖注入框架上的,所以它引用“依赖注入”框架的抽象包Microsoft.Extensions.DependencyInjection.Abstractions
  • 如果是控制台程序,为了能使用“选项模型”,我们还要再手动引入“依赖注入”框架的实现包:Microsoft.Extensions.DependencyInjection
  • 如果我们想让“配置”为“选项模型”提供数据来源,那么我们需要手动引入扩展包:Microsoft.Extensions.Options.ConfigurationExtensions

二、使用选项模型(最简单的用法)

  • 第一步:新建控制台程序,添加包:Microsoft.Extensions.OptionsMicrosoft.Extensions.DependencyInjection
    在这里插入图片描述
    在这里插入图片描述
  • 第二步:控制台编写代码:
    namespace ConsoleApp7
    {
        public class Person
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public override string ToString()
            {
                return $"(Person=>Id:{Id},Name:{Name})";
            }
        }
        public class Student
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public override string ToString()
            {
                return $"(Student=>Id:{Id},Name:{Name})";
            }
        }
        class Program
        {
            public static void Main(string[] args)
            {
                ServiceCollection services = new ServiceCollection();
                //向依赖框架中注入选项模型服务
                services.AddOptions();
                //注入Person模型的配置逻辑
                services.Configure<Person>(person =>
                {
                    person.Id = 1;
                    person.Name = "小明";
                    Console.WriteLine("services.Configure<Person>(person =>{})");
                });
                //注入Student模型的配置逻辑
                services.Configure<Student>(student =>
                {
                    student.Id = 1;
                    student.Name = "小刚";
                    Console.WriteLine("services.Configure<Student>(student =>{})");
                });
                //创建依赖容器
                var provider = services.BuildServiceProvider();
                //获取选项模型Person
                var option = provider.GetService<IOptions<Person>>();
                var person = option.Value;
                //获取选项模型Student
                var option2 = provider.GetService<IOptions<Student>>();
                var student = option2.Value;
    
                Console.WriteLine(person);
                Console.WriteLine(student);
                Console.WriteLine("ok");
                Console.ReadLine();
            }
        }
    }
    
  • 第三步:运行效果:
    在这里插入图片描述

三、选项模型用法剖析

选项模型分类:

  • 第一:按类别,比如这个模型是Person的还是Student的。
  • 第二:按名称,即使同一个类别的模型(如:Person),也要再区分名称(比如:“小明”、“小刚”等)

根据上面分析的选项模型的分类,我们在向依赖容器注入选项模型的初始化逻辑时有如下几种方法:

注入初始化逻辑:

  • services.Configure<Student>(student =>{}):名称为空字符串初始化逻辑
  • services.Configure<Student>("小明",student =>{}):名称为“小明”的初始化逻辑
  • services.ConfigureAll<Student>(student =>{}):为所有名称的模型注入初始化逻辑

注入初始化后逻辑:

  • services.PostConfigure<Student>(student =>{}):名称为空字符串初始化后逻辑
  • services.PostConfigure<Student>("小明",student =>{}):名称为空字符串初始化后逻辑
  • services.PostConfigureAll<Student>(student =>{}):为所有名称的模型注入初始化后逻辑

注入初始化类:

  • services.ConfigureOptions<PersonConfig>();:将Person类的初始化逻辑放到类PersonConfig中,其中PersonConfig的定义如下:
    在这里插入图片描述

上面列举的注册方法的源码如下:
在这里插入图片描述
除了上面直接在IServiceCollection上扩展的方法外,我们也可以使用OptionsBuilder进行构建:

var services = new ServiceCollection();
varoptionBuilder = services.AddOptions<Person>("小明");
optionBuilder.Configure(person => { });
optionBuilder.PostConfigure(person => { });
optionBuilder.Validate(person => true);

四、选项模型的验证

一般我们用Options模型承载用户的配置,那么既然是配置就可能会有配置出错的问题,微软为我们提供了选项模型验证的功能(optionBuilder.Validate(Action<TModel>action)),借助这个功能,我们就可以让调用者在获取选项模型实例的时候就能抛出异常,而不是在使用到某一配置项的时候才意外抛出错误。

具体的使用方法看下面实例:

class Program
{
    static void Main(string[] args)
    {
        var services = new ServiceCollection();
        var optionBuilder = services.AddOptions<BaseConfig>()
            .Configure(config =>
            {
                config.ThirdApiAddress = ReadThridApiAddress();
            })
            .Validate(config => config.ThirdApiAddress != null && config.ThirdApiAddress.StartsWith("http"), "第三方接口地址配置错误,请检查!");
        var provider = services.BuildServiceProvider();
        var option = provider.GetRequiredService<IOptions<BaseConfig>>().Value;
        Console.WriteLine("Hello World!");
    }

    public static string ReadThridApiAddress()
    {
        return "htt://www.xxxx.com/api/user/verfiy";
    }
}

public class BaseConfig
{
    public string ThirdApiAddress { get; set; }
}

运行后报错如下:
在这里插入图片描述

五、集成配置模型

比如,我们想从Json配置文件中读取配置内容并转化成选项模型实例,那么我们需要引用Json配置包Microsoft.Extensions.Configuration.Json,还要配置到选项模型的绑定包Microsoft.Extensions.Options.ConfigurationExtensions,如下图所示:
在这里插入图片描述
接下来就看他们结合的代码:

class Program
 {
     static void Main(string[] args)
     {
         var services = new ServiceCollection();
         var configBuilder = new ConfigurationBuilder();
         //下面两种写法都可以
         var config = configBuilder.AddJsonFile("conf.json").Build();
         //services.AddOptions<BaseConfig>().Bind(config);
         services.Configure<BaseConfig>(config);
         var provider = services.BuildServiceProvider();
         var baseConfig = provider.GetRequiredService<IOptions<BaseConfig>>().Value;
         Console.WriteLine($"baseConfig.ThirdApiAddress={baseConfig.ThirdApiAddress}");


         Console.WriteLine("Hello World!");
     }

     public static string ReadThridApiAddress()
     {
         return "htt://www.xxxx.com/api/user/verfiy";
     }
 }

 public class BaseConfig
 {
     public string ThirdApiAddress { get; set; }
 }

注意:上面代码中的conf.json文件,自己直接在工程根目录下新建个就可以,内容如下:

{
 "ThirdApiAddress": "http://www.xxxx.com/user/verify"
}

另外,在vs中设置conf.json文件属性为输出到目录。

代码运行如下:

在这里插入图片描述

六、具名选项模型(IOptionsSnapshot<TOptions>

这个具名选项模型其实上面已经说过了,在《三、选项模型用法剖析》中,我们说选项模型按照“类别”和“名称”进行分类,那么具有名称的选项模型就是具名选项模型

它的使用方式如下:

class Program
{
    static void Main(string[] args)
    {
        var services = new ServiceCollection();
        services.Configure<BaseConfig>("development", conf =>
         {
             conf.ThirdApiAddress = "http://www.xxxx.conm/test";
         });
        services.Configure<BaseConfig>("production", conf =>
         {
             conf.ThirdApiAddress = "http://www.xxxx.com/api/user/verify";
         });
        var provider = services.BuildServiceProvider();
        var snapConf = provider.GetRequiredService<IOptionsSnapshot<BaseConfig>>();
        var dev = snapConf.Get("development");
        Console.WriteLine($"development:ThirdApiAddress={dev.ThirdApiAddress}");
        var prod = snapConf.Get("production");
        Console.WriteLine($"production:ThirdApiAddress={prod.ThirdApiAddress}");

        Console.WriteLine("Hello World!");
    }
}

public class BaseConfig
{
    public string ThirdApiAddress { get; set; }
}

运行效果如下图:
在这里插入图片描述

七、根据配置文件实时更新选项模型()

在上面我们已经将json配置文件中的内容绑定到了选项模型BaseConfig中,现在我们要实现的时:在程序运行的时候,如果conf.json文件的内容改变了,那么重新获取的BaseConfig实例也会发生变化。
我们还是使用上面的conf.json配置文件,具体代码如下:

class Program
{
    static void Main(string[] args)
    {
        var services = new ServiceCollection();
        var config = new ConfigurationBuilder()
            .AddJsonFile("conf.json", optional: false, reloadOnChange: true)
            .Build(); ;
        services.Configure<BaseConfig>(config);
        var provider = services.BuildServiceProvider();
        while (true)
        {
            var optionsMonitor = provider.GetRequiredService<IOptionsMonitor<BaseConfig>>();
            Console.WriteLine($"optionsMonitor.CurrentValue.ThirdApiAddress={optionsMonitor.CurrentValue.ThirdApiAddress}");
            Console.WriteLine("请修改conf.json内容后按回车...");
            Console.ReadLine();
        }
    }
}

public class BaseConfig
{
    public string ThirdApiAddress { get; set; }
}

此时conf.json中的内容如下:

{
  "ThirdApiAddress": "http://www.xxxx.com/user/verify"
}

我们运行应用程序,可以看到将当前的值打印出来了,按照提示,我们改变conf.json文件的内容,然后敲回车,即可查看到json配置文件的变化已经转换到了选项模型上,如下图所示:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jackletter

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值