跟我一起学.NetCore之Options实例演示及分析
前言
來啦!來啦!上一節(jié)一堆代碼,是不是感覺甚是無味啊?沒關(guān)系,這里結(jié)合上一節(jié)內(nèi)容專注舉例演示,絕不廢話!走起~~~~~
正文
老規(guī)矩,一個WebApi項(xiàng)目走起,項(xiàng)目結(jié)構(gòu)如下:
上一節(jié)中提到,Options是基于依賴注入的,所以我們需要將相關(guān)服務(wù)進(jìn)行注冊,如下:
注冊完成之后就可以直接用啦,這里新建了一個ComputerController進(jìn)行測試:
運(yùn)行走起:
通常在使用的時候,相關(guān)服務(wù)的配置會放到配置文件中,比如數(shù)據(jù)庫連接字符串等,所以在注冊的時候?qū)⒊跏蓟膬?nèi)容從配置中獲取即可,如下:
跑起來~~~
通過以上的實(shí)例演示,可能會好奇為啥不直接用Configuration,我是這樣理解的:通過Options的話,服務(wù)不限制使用初始化方式,根據(jù)需求選擇,如果使用配置文件取值,服務(wù)也和配置沒有直接關(guān)系,從而使得服務(wù)的使用沒有過多限制;另外服務(wù)內(nèi)部使用也比較便捷,就單純操作Options對象;
哎呀,又來這一套,使用太簡單了,來點(diǎn)真貨唄,此時應(yīng)該有小伙伴按捺不住了問:Options的相關(guān)服務(wù)是怎么注冊的,初始化和上一節(jié)講的初始化有啥關(guān)系??來來來,不急,以下慢慢品~~~
那從哪開始呢??
上一小節(jié)說的TOptions對象創(chuàng)建的三大步,創(chuàng)建內(nèi)部直接New了(忘了的可以回顧一下上一節(jié):跟我一起學(xué).NetCore之選項(xiàng)(Options)核心類型簡介),那這里就從注冊初始化的地方下手:
Services.Configure方法中肯定有事,不然一句代碼咋就讓后面使用如此便捷呢,來,直接看源代碼:
// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.using System; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Options;namespace Microsoft.Extensions.DependencyInjection {/// <summary>/// Extension methods for adding configuration related options services to the DI container./// </summary>public static class OptionsConfigurationServiceCollectionExtensions{// 挨著的這三個方法都是IServiceCollection 的擴(kuò)展方法public static IServiceCollection Configure<TOptions>(this IServiceCollection services, IConfiguration config) where TOptions : class=>?services.Configure<TOptions>(Options.Options.DefaultName,?config);public static IServiceCollection Configure<TOptions>(this IServiceCollection services, string name, IConfiguration config) where TOptions : class=>?services.Configure<TOptions>(name,?config,?_?=>?{?});?public static IServiceCollection Configure<TOptions>(this IServiceCollection services, IConfiguration config, Action<BinderOptions> configureBinder)where TOptions : class=>?services.Configure<TOptions>(Options.Options.DefaultName,?config,?configureBinder);// 關(guān)鍵方法public static IServiceCollection Configure<TOptions>(this IServiceCollection services, string name, IConfiguration config, Action<BinderOptions> configureBinder)where TOptions : class{if (services == null){throw new ArgumentNullException(nameof(services));}if (config == null){throw new ArgumentNullException(nameof(config));}//?注冊O(shè)ptions相關(guān)服務(wù),這是關(guān)鍵方法services.AddOptions();// IOptionsChangeTokenSource 注冊,后續(xù)用于通知services.AddSingleton<IOptionsChangeTokenSource<TOptions>>(new ConfigurationChangeTokenSource<TOptions>(name, config));//?注冊從配置系統(tǒng)中獲取配置值的服務(wù)return services.AddSingleton<IConfigureOptions<TOptions>>(new NamedConfigureFromConfigurationOptions<TOptions>(name, config, configureBinder));}} }上面代碼中services.AddOptions()是關(guān)鍵方法,再翻代碼瞅瞅:
public static class OptionsServiceCollectionExtensions{//?恍然大悟吧,就是這個擴(kuò)展方法,把服務(wù)都注冊好了,所以使用的時候才直接注入即可public static IServiceCollection AddOptions(this IServiceCollection services){if (services == null){throw new ArgumentNullException(nameof(services));}//?使用TryAdd的注入方式,為了對應(yīng)服務(wù)類型只注冊一次//?注冊IOptions<>,實(shí)現(xiàn)類是OptionsManager<>services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(OptionsManager<>)));// 注冊IOptionsSnapshot<>,實(shí)現(xiàn)類是OptionsManager<>services.TryAdd(ServiceDescriptor.Scoped(typeof(IOptionsSnapshot<>), typeof(OptionsManager<>)));// 注冊IOptionsMonitor<>,實(shí)現(xiàn)類是OptionsMonitor<>services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitor<>), typeof(OptionsMonitor<>)));//?注冊IOptionsFactory<>?,實(shí)現(xiàn)類是OptionsFactory<>services.TryAdd(ServiceDescriptor.Transient(typeof(IOptionsFactory<>), typeof(OptionsFactory<>)));//?注冊IOptionsMonitorCache<>,?實(shí)現(xiàn)類是OptionsCache<>services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitorCache<>), typeof(OptionsCache<>)));return services;}//?對應(yīng)創(chuàng)建TOption?初始化的兩小步,步驟1Configurepublic static IServiceCollection Configure<TOptions>(this IServiceCollection services, string name, Action<TOptions> configureOptions)where TOptions : class{if (services == null){throw new ArgumentNullException(nameof(services));}if (configureOptions == null){throw new ArgumentNullException(nameof(configureOptions));}services.AddOptions();services.AddSingleton<IConfigureOptions<TOptions>>(new ConfigureNamedOptions<TOptions>(name, configureOptions));return services;}//?對應(yīng)創(chuàng)建TOption?初始化的兩小步,步驟2 PostConfigurepublic static IServiceCollection PostConfigure<TOptions>(this IServiceCollection services, string name, Action<TOptions> configureOptions)where TOptions : class{if (services == null){throw new ArgumentNullException(nameof(services));}if (configureOptions == null){throw new ArgumentNullException(nameof(configureOptions));}services.AddOptions();services.AddSingleton<IPostConfigureOptions<TOptions>>(new PostConfigureOptions<TOptions>(name, configureOptions));return services;}......省略了其他方法....}看了以上代碼,是不是豁然開朗了,Options的相關(guān)服務(wù)注冊都是在AddOptions擴(kuò)展方法中完成的,至于其中的Configure和PostConfigure實(shí)則對應(yīng)著上一節(jié)的創(chuàng)建Options初始化的過程;以上注冊的核心類型的生命周期需要了解一下,后面舉例會關(guān)聯(lián)到:
IOptions<>:Singleton - 單例模式
IOptionsSnapshot<>:Scoped - 作用域范圍模式
IOptionsMonitor<>:?Singleton - 單例模式
Configure是注冊時進(jìn)行初始化,而PostConfigure一般會用在讀取到配置數(shù)據(jù)之后對數(shù)據(jù)進(jìn)行加工,如下:
接下來說熱更新,也就是配置改變,服務(wù)對應(yīng)的Options能獲取最新的值,代碼稍微優(yōu)化一下:
第一次訪問,第二次訪問前進(jìn)行配置文件修改,然后在訪問,運(yùn)行結(jié)果如下:
可以看到,配置數(shù)據(jù)改變時,IOptionsSnapshot<>和IOptionsMonitor<>都能獲取到最新的值,IOptions<>沒有獲取到,簡單分析一下:
IOptons<>和IOptionsSnapshot<>其實(shí)內(nèi)部是一樣的,只是注入的生命周期不一樣,前者是單例,后者是Scoped,這樣就使得后者每次請求都是不同的IOptionsSnapshot對象,從而就能獲取最新的值;而單例IOptions對象一致不變;那為什么單例的IOptionsMonitor類型能改變呢,那是因?yàn)镮OptionsMonitor提供了監(jiān)聽改變的功能,上一節(jié)有簡單說明;
如果在配置數(shù)據(jù)改變時,需要通知Option怎么辦呢?通過IOptionsMonitor<TOptions>的OnChange監(jiān)聽改變,如下:
運(yùn)行看結(jié)果,首先請求一次,然后修改配置文件,就能看到實(shí)時監(jiān)控了:
最后來說說Options驗(yàn)證(也就是創(chuàng)建TOptions的第三步),主要是避免不合法的配置,導(dǎo)致程序在運(yùn)行時業(yè)務(wù)邏輯出錯才能發(fā)現(xiàn)錯誤,從而導(dǎo)致程序迭代周期頻繁,用戶體驗(yàn)差;驗(yàn)證有以下三種方式:
直接注冊驗(yàn)證函數(shù)
實(shí)現(xiàn)IValidateOptions<TOptions>進(jìn)行驗(yàn)證
使用注解(Microsoft.Extensions.Options.DataAnnotations)
這里用三種方式分別依次驗(yàn)證Name,Cores,MemorySize 三個配置項(xiàng),如下:
直接注冊驗(yàn)證函數(shù)
實(shí)現(xiàn)IValidateOptions<TOptions>進(jìn)行驗(yàn)證
使用注解(Microsoft.Extensions.Options.DataAnnotations)
三種驗(yàn)證方式是相互獨(dú)立的,可以單獨(dú)使用,也可以組合使用;以上每一種方式對應(yīng)圖片內(nèi)容;
運(yùn)行訪問不合法就報錯,這樣就避免配置不合法,導(dǎo)致業(yè)務(wù)邏輯不合法,如下
總結(jié)
Options(選項(xiàng))的常規(guī)用法暫時就說這么多了,結(jié)合上一小節(jié)是不是感覺清晰多了~~~下一節(jié)說說日志(ILogger)
總結(jié)
以上是生活随笔為你收集整理的跟我一起学.NetCore之Options实例演示及分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 听说用 C# 写 TensorFlow
- 下一篇: 跟我一起学.NetCore之日志(Log