加入收藏 | 设为首页 | 会员中心 | 我要投稿 核心网 (https://www.hxwgxz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程 > 正文

详解ASP.NET Core3.0 配置的Options模式

发布时间:2020-08-21 13:35:43 所属栏目:编程 来源:网络整理
导读:这篇文章主要介绍了详解ASP.NET Core3.0 配置的Options模式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面

另外的两个Configure方法就是上一节例子中用到的将具体的Theme注册到Options中的方法了。二者的区别就是是否为配置的option命名,而第一个Configure方法就未命名的方法,通过上面的代码可知它实际上是传入了一个默认的Options.Options.DefaultName作为名称,这个默认值是一个空字符串,也就是说,未命名的Option相当于是被命名为空字符串。最终都是按照已命名的方式也就是第二个Configure方法进行处理。还有一个ConfigureAll方法,它是传入了一个null作为Option的名称,也是交由第二个Configure处理。

在第二个Configure方法中仍调用了一次AddOptions方法,然后才是将具体的类型进行注入。AddOptions方法中采用的都是TryAdd方法进行注入,已被注入的不会被再次注入。接下来注册了一个IConfigureOptions<TOptions>接口,对应的实现是ConfigureNamedOptions<TOptions>(name, configureOptions),它的代码如下:

public class ConfigureNamedOptions<TOptions> : IConfigureNamedOptions<TOptions> where TOptions : class { public ConfigureNamedOptions(string name, Action<TOptions> action) { Name = name; Action = action; } public string Name { get; } public Action<TOptions> Action { get; } public virtual void Configure(string name, TOptions options) { if (options == null) { throw new ArgumentNullException(nameof(options)); } // Null name is used to configure all named options. if (Name == null || name == Name) { Action?.Invoke(options); } } public void Configure(TOptions options) => Configure(Options.DefaultName, options); }

它在构造方法中存储了配置的名称(Name)和创建方法(Action),它的两个Configure方法用于在获取Options的值的时候执行对应的Action来创建实例(例如示例中的Theme)。在此时不会被执行。所以在此会出现3中类型的ConfigureNamedOptions,分别是Name值为具体值的、Name值为为空字符串的和Name值为null的。这分别对应了第一节的例子中的为Option命名的Configure方法、不为Option命名的Configure方法、以及ConfigureAll方法。

此处用到的OptionsServiceCollectionExtensions和ConfigureNamedOptions对应的是通过代码直接注册Option的方式,例如第一节例子中的如下方式:

services.Configure<Theme>("ThemeBlack", theme => { new Theme { Color = "#000000", Name = "Black" }; });

如果是以Configuration作为数据源的方式,例如如下代码

services.Configure<Theme>("ThemeBlue", Configuration.GetSection("Themes:0"));

用到的是OptionsServiceCollectionExtensions和ConfigureNamedOptions这两个类的子类,分别为OptionsConfigurationServiceCollectionExtensions和NamedConfigureFromConfigurationOptions两个类,通过它们的名字也可以知道是专门用于采用Configuration作为数据源用的,代码类似,只是多了一条关于IOptionsChangeTokenSource的依赖注入,作用是将Configuration的关于数据源变化的监听和Options的关联起来,当数据源发生改变的时候可以及时更新Options中的值,主要的Configure方法代码如下:

public static IServiceCollection Configure<TOptions>(this IServiceCollection services, string name, IConfiguration config, Action<BinderOptions> configureBinder) where TOptions : class { //省略验证代码 services.AddOptions(); services.AddSingleton<IOptionsChangeTokenSource<TOptions>>(new ConfigurationChangeTokenSource<TOptions>(name, config)); return services.AddSingleton<IConfigureOptions<TOptions>>(new NamedConfigureFromConfigurationOptions<TOptions>(name, config, configureBinder)); }

同样还有PostConfigure和PostConfigureAll方法,和Configure、ConfigureAll方法类似,只不过注入的类型为IPostConfigureOptions<TOptions>。

2. Options值的获取

Option值的获取也就是从依赖注入容器中获取相应实现的过程。通过依赖注入阶段,已经知道了IOptions<>和IOptionsSnapshot<>对应的实现是OptionsManager<>,就以OptionsManager<>为例看一下依赖注入后的服务提供过程。OptionsManager<>代码如下:

public class OptionsManager<TOptions> : IOptions<TOptions>, IOptionsSnapshot<TOptions> where TOptions : class, new() { private readonly IOptionsFactory<TOptions> _factory; private readonly OptionsCache<TOptions> _cache = new OptionsCache<TOptions>(); public OptionsManager(IOptionsFactory<TOptions> factory) { _factory = factory; } public TOptions Value { get { return Get(Options.DefaultName); } } public virtual TOptions Get(string name) { name = name ?? Options.DefaultName; return _cache.GetOrAdd(name, () => _factory.Create(name)); } }

它有IOptionsFactory<TOptions>和OptionsCache<TOptions>两个重要的成员。如果直接获取Value值,实际上是调用的另一个Get(string name)方法,传入了空字符串作为name值。所以最终值的获取还是在缓存中读取,这里的代码是_cache.GetOrAdd(name, () => _factory.Create(name)),即如果缓存中存在对应的值,则返回,如果不存在,则由_factory去创建。OptionsFactory<TOptions>的代码如下:

public class OptionsFactory<TOptions> : IOptionsFactory<TOptions> where TOptions : class, new() { private readonly IEnumerable<IConfigureOptions<TOptions>> _setups; private readonly IEnumerable<IPostConfigureOptions<TOptions>> _postConfigures; private readonly IEnumerable<IValidateOptions<TOptions>> _validations; public OptionsFactory(IEnumerable<IConfigureOptions<TOptions>> setups, IEnumerable<IPostConfigureOptions<TOptions>> postConfigures) : this(setups, postConfigures, validations: null) { } public OptionsFactory(IEnumerable<IConfigureOptions<TOptions>> setups, IEnumerable<IPostConfigureOptions<TOptions>> postConfigures, IEnumerable<IValidateOptions<TOptions>> validations) { _setups = setups; _postConfigures = postConfigures; _validations = validations; } public TOptions Create(string name) { var options = new TOptions(); foreach (var setup in _setups) { if (setup is IConfigureNamedOptions<TOptions> namedSetup) { namedSetup.Configure(name, options); } else if (name == Options.DefaultName) { setup.Configure(options); } } foreach (var post in _postConfigures) { post.PostConfigure(name, options); } if (_validations != null) { var failures = new List<string>(); foreach (var validate in _validations) { var result = validate.Validate(name, options); if (result.Failed) { failures.AddRange(result.Failures); } } if (failures.Count > 0) { throw new OptionsValidationException(name, typeof(TOptions), failures); } } return options; } }

(编辑:核心网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读