当前位置 : 主页 > 编程语言 > 其它开发 >

Net6 Configuration & Options 源码分析 Part2 Options 模型使用与源码分析

来源:互联网 收集:自由互联 发布时间:2022-05-21
Net6 Configuration Options 源码分析 Part2 Options 第二部分主要记录 Options 模型 OptionsConfigurationServiceCollectionExtensions类提供了对 Options 模型 与 配置系统 的Configure方法的扩展 1. 直接使用Options直接
Net6 Configuration & Options 源码分析 Part2 Options

第二部分主要记录Options 模型
OptionsConfigurationServiceCollectionExtensions类提供了对Options 模型配置系统的Configure方法的扩展

1. 直接使用Options 直接使用Options

在Starup ConfigService中经常会看到把一个拉姆达注册成配置项例如:.Configure<Profile>(it ->it.age = 18),我们称这个拉姆达为Configure Action,其实这是使用了一个包装类,包装你的Configure Action委托,并把这个类的实例注册到Service容器中。它实现IOptions与拉姆达如何映射的,这一切由OptionsServiceCollectionExtensionsOptionsFacotry等实现的。你也可以直接像下面这样使用

var profile = new Servicecollection ().Addoptions().Configure<Profile>(it ->it.age = 18).BuildServiceProvider().GetRequiredService<IOptions<Profile>>().Value;
配置服务注册源码分析/Configure Action包装类的注册

OptionsServiceCollectionExtensions 作为配置服务的扩展类下面有三种类型的扩展方法分别是Configure、PostConfigure、AddOptions,前两所对应的服务为IConfigureOptions与IPostConfigureOptions 区别仅仅是为了实现配置Configure Action的执行时机,IPostConfigureOptions会后执行,而AddOptions本质上还是注册前两种,注册成面上看起来AddOptions注册的Configure Action具有了参数可以访问其它DI内服务。
注意:就算你使用了三个注册方式注册一次或多次对同一个TOptions进行注册,他们其实是操作的同一个TOptions给你。这体现在OptionsFactory.Create上,也是我们想要的效果。

以下代码为Configure、PostConfigure的服务注册逻辑。

Configure、PostConfigure、扩展方法注册的 Configure Action会由IConfigureOptions与IPostConfigureOptions 接口对应的包装类进行包装。属性均是Action,Configure Action的执行是在Configure方法中。IConfigureOptions与IPostConfigureOptions的大量泛型类是为了实现使用AddOptions注册的Configure Action访问其它DI内服务。会在下面单独记录。

值得注意的是ConfigureAll如果你此方法去注入一个Name 为null 的 Configure Action包装类逻辑体现在ConfigureNamedOptions.Configure/PostConfigureOptions.Configure方法上。,官方说法:“Configure ALL options instances, both named and default”翻译后扩展方法将配置应用于所有选项,包括命名实例和默认实例。

Configure -> ConfigureNamedOptions
PostConfigure -> PostConfigureOptions

public static class OptionsServiceCollectionExtensions
{
    ...
   public static IServiceCollection Configure<TOptions>(this IServiceCollection services!!, string? name, Action<TOptions> configureOptions!!)
            where TOptions : class
    {
        services.AddOptions();
        services.AddSingleton<IConfigureOptions<TOptions>>(new ConfigureNamedOptions<TOptions>(name, configureOptions));
        return services;
    }

    public static IServiceCollection PostConfigure<TOptions>(this IServiceCollection services!!, string? name, Action<TOptions> configureOptions!!)
    where TOptions : class
    {
        services.AddOptions();
        services.AddSingleton<IPostConfigureOptions<TOptions>>(new PostConfigureOptions<TOptions>(name, configureOptions));
        return services;
    }
    ...
}
public class ConfigureNamedOptions<TOptions> : IConfigureNamedOptions<TOptions> where TOptions : class
{
    public ConfigureNamedOptions(string? name, Action<TOptions>? action)
    {
        Name = name;
        Action = action;
    }
    public virtual void Configure(string? name, TOptions options!!)
    {
        // Null name is used to configure all named options.// Name的过滤以及Configure 逻辑就是在这里体现的
        if (Name == null || name == Name)
        {
            Action?.Invoke(options);
        }
    }
}
public class PostConfigureOptions<TOptions> : IPostConfigureOptions<TOptions> where TOptions : class
{
    public PostConfigureOptions(string? name, Action<TOptions>? action)
    {
        Name = name;
        Action = action;
    }

    public virtual void PostConfigure(string? name, TOptions options!!)
    {
        if (Name == null || name == Name)
        {
            Action?.Invoke(options);
        }
    }
}

以下代码为AddOptions 的服务注册逻辑。

AddOptions:此方法会帮你构建一个OptionsBuilder,并非向Service容器注入,而是利用其builder类的Configure方法向Service容器具体注入。其下面的大量重载 Configure方法会帮你创建基于IConfigureNamedOptions/IPostConfigureOptions不同数量的泛型类. 其目的就是为了解决在“ Configure Action”中使用其它服务。
设计思路很好可以参考

整体思路大概是,先用AddOptions扩展方法创建了一个OptionsBuilder对象,然后调用它重载方法Configure<TService...>去创建具有多个泛型的ConfigureNamedOptions对象。ConfigureNamedOptions的Configure在执行Action委托时会用serviceProvider获取到TService泛型服务。作为参数传入Action委托。这样委托在真正被执行时就会拿到对应的服务。

public static class OptionsServiceCollectionExtensions
{
    ...
    public static OptionsBuilder<TOptions> AddOptions<TOptions>(this IServiceCollection services!!, string? name)
        where TOptions : class
    {
        services.AddOptions();
        return new OptionsBuilder<TOptions>(services, name);
    }
    ...
}

public class OptionsBuilder<TOptions> where TOptions : class
{
    ...
    public virtual OptionsBuilder<TOptions> Configure<TDep>(Action<TOptions, TDep> configureOptions!!) where TDep : class
    {
        Services.AddTransient<IConfigureOptions<TOptions>>(sp =>
            new ConfigureNamedOptions<TOptions, TDep>(Name, sp.GetRequiredService<TDep>(), configureOptions));
        return this;
    }
    ...
}

public class ConfigureNamedOptions<TOptions, TDep> : IConfigureNamedOptions<TOptions> {
    ...
    public ConfigureNamedOptions(string? name, TDep dependency, Action<TOptions, TDep>? action)
    {
        Name = name;
        Action = action;
        Dependency = dependency;
    }

    public virtual void Configure(string? name, TOptions options!!)
    {
        // Null name is used to configure all named options.
        if (Name == null || name == Name)
        {
            Action?.Invoke(options, Dependency);
        }
    }
    ...
}

总结 注入到服务的扩展方法(OptionsServiceCollectionExtensions) 服务类 服务实现类 使用 生命周期 备注 Configure IConfigureOptions ConfigureNamedOptions 被OptonsFactory使用 Singleton ConfigureAll,在IPostConfigureOptions前执行Configure Action PostConfigure IPostConfigureOptions PostConfigureOptions 被OptonsFactory使用 Singleton ConfigureAll 在IConfigureOptions后执行Configure Action AddOptions IConfigureOptions/IPostConfigureOptions ConfigureNamedOptions/PostConfigureOptions 被OptonsFactory使用 Singleton 辅助注入一个可以访问其它服务的Configure Action 配置Otpns服务的使用

首先在固有想法上注入的服务直接会拿来使用。而在这里注入的均为IConfigureOptions/IPostConfigureOptions服务我们管他们叫Configure Action的包装类,而要使用这些服务是通过IOptions/IOptionsSnapshot/IOptionsMonitor去获得。我们称这三个服务为OptionsManger类

注册的基础服务(OptionsServiceCollectionExtensions.AddOptions)

这里整理出来IOptions/IOptionsSnapshot/IOptionsMonitor三种OptionsManger的区别。
服务类|服务实现类|使用|生命周期|备注|
---|:--

上一篇:构建 Go 应用 docker 镜像的十八种姿势
下一篇:没有了
网友评论