日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > asp.net >内容正文

asp.net

ASP.NET Core 沉思录 - ServiceProvider 的二度出生

發(fā)布時(shí)間:2023/12/4 asp.net 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ASP.NET Core 沉思录 - ServiceProvider 的二度出生 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

ASP.NET Core 終于將幾乎所有的對(duì)象創(chuàng)建工作都和依賴注入框架集成了起來(lái)。并對(duì)大部分的日常工作進(jìn)行了抽象。使得整個(gè)框架擴(kuò)展更加方便。各個(gè)部分的集成也更加容易。今天我們要思考的部分仍然是從一段每一個(gè)工程中都大同小異的代碼開(kāi)始的。

IWebHostBuilder?CreateWebHostBuilder(string[]?args)
{
????return?new?WebHostBuilder()
????????.UseKestrel(ko?=>?ko.AddServerHeader?=?false)
????????.ConfigureAppConfiguration(cb?=>?cb.AddCommandLine(args))
????????.ConfigureLogging(lb?=>?{...})
????????.UseStartup<Startup>();
}

0 太長(zhǎng)不讀

  • ASP.NET Core 的初始化包含了兩個(gè)步驟:第一個(gè)步驟是 Hosting 相關(guān)服務(wù)的初始化過(guò)程,初始化完畢之后創(chuàng)建了第一個(gè) IServiceProvider 對(duì)象;第二步是 Application 相關(guān)服務(wù)的初始化過(guò)程。而 Application 的初始化過(guò)程可以注入 Hosting 相關(guān)的服務(wù)。之后,通過(guò) IStartup.ConfigureServices 方法創(chuàng)建了第二個(gè) IServiceProvider 對(duì)象。

  • 初始化過(guò)程中創(chuàng)建的兩個(gè) IServiceProvider 均會(huì)跟隨 WebHost 的銷毀而銷毀。

  • 通過(guò) Startup 類型的構(gòu)造函數(shù)注入的實(shí)例是由 Hosting 初始化階段創(chuàng)建的 IServiceProvider 創(chuàng)建的。只能注入 Hosting 初始化階段添加的類型。且最好不要使用大量消耗資源的類型。

  • 可以在 Startup.Configure 方法中添加其他參數(shù),這樣會(huì)使用 Application 的一個(gè) Scope 下的 IServiceProvider 進(jìn)行注入,且在方法調(diào)用完畢之后該 Scope 即被銷毀。因此該方法內(nèi)可以創(chuàng)建資源占用量較高的需要 Dispose 的類型實(shí)例而不造成泄露。

1 WebHost 的構(gòu)建主要就是向 `IServiceCollection` 中添加服務(wù)

之前提到過(guò),任何 Framework 只有兩件事情,第一件事情就是對(duì)象怎么創(chuàng)建,第二件事情就是如何將這些創(chuàng)建出來(lái)的對(duì)象塞到 Framework 處理流水線中。因此 ASP.NET Core 也是這樣。在應(yīng)用程序啟動(dòng)的時(shí)候,我們會(huì)在 WebHostBuilder.Build 方法調(diào)用之前進(jìn)行各種各樣的操作,雖然我們調(diào)用的大部分操作都是擴(kuò)展方法(例如上述代碼中的 UseXxx,和 ConfigureLogging),但是歸根結(jié)底會(huì)調(diào)用 IWebHostBuilder 的以下方法:

IWebHostBuilder?ConfigureAppConfiguration(Action<WebHostBuilderContext,?IConfigurationBuilder>?configureDelegate);
IWebHostBuilder?ConfigureServices(Action<IServiceCollection>?configureServices);
IWebHostBuilder?ConfigureServices(Action<WebHostBuilderContext,?IServiceCollection>?configureServices);

不論調(diào)哪一個(gè)方法,它們做的事情其實(shí)都是一件。就是告訴應(yīng)用程序,我到底有哪些對(duì)象需要?jiǎng)?chuàng)建,如何創(chuàng)建這些對(duì)象,以及其生存期如何管理。從技術(shù)角度上來(lái)說(shuō),就是將需要?jiǎng)?chuàng)建的對(duì)象類型添加到 IServiceCollection 中。如果感興趣的同學(xué)可以看看 WebHostBuilder 的實(shí)現(xiàn)代碼(https://github.com/aspnet/AspNetCore/blob/master/src/Hosting/Hosting/src/WebHostBuilder.cs),就更加清晰了。

例如,以 ConfigureLogging 為例,代碼請(qǐng)參見(jiàn)這里(https://github.com/aspnet/Extensions/blob/master/src/Logging/Logging/src/LoggingServiceCollectionExtensions.cs):

public?static?IWebHostBuilder?ConfigureLogging(
????this?IWebHostBuilder?hostBuilder,?Action<WebHostBuilderContext,?
????ILoggingBuilder>?configureLogging
)
{
????return?hostBuilder.ConfigureServices((context,?collection)?=>?
????????collection.AddLogging(builder?=>?configureLogging(context,?builder)));
}

public?static?IServiceCollection?AddLogging(
????this?IServiceCollection?services,?
????Action<ILoggingBuilder>?configure
)
{
????if?(services?==?null)?{?throw?new?ArgumentNullException(nameof(services));?}

????services.AddOptions();
????services.TryAdd(ServiceDescriptor.Singleton<ILoggerFactory,?LoggerFactory>());
????services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>),?typeof(Logger<>)));
????services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<LoggerFilterOptions>>(
????????new?DefaultLoggerLevelConfigureOptions(LogLevel.Information)));
????configure(new?LoggingBuilder(services));
????return?services;
}

可以看到實(shí)際上就是將 IOptions<>、IOptionsSnapshot<>、IOptionsMonitor<>、IOptionsFactory<>、IOptionsMonitorCache<> 以及 ILoggerFactory、ILogger<>、IConfigureOptions<LoggerFilterOptions> 添加到 IServiceCollection 中的過(guò)程。有關(guān)日志的內(nèi)容我們會(huì)在另一篇文章中介紹。

2 Startup 初始化時(shí)為什么又能注入又有 `IServiceCollection` 呢

在 WebHost 的構(gòu)建過(guò)程中,十有八九會(huì)出現(xiàn) UseStartup 這句話(如果不出現(xiàn)這句話,那么很大程度上使用了 Configure 擴(kuò)展方法)。Startup 是整個(gè) Web 應(yīng)用程序的起點(diǎn)。應(yīng)用程序(Web App)托管在宿主(Hosting Environment)中。那么它應(yīng)當(dāng)是在初始化的最終階段執(zhí)行的。我們來(lái)觀察一下它的典型結(jié)構(gòu):

public?class?Startup
{
????public?void?ConfigureServices(IServiceCollection?services)
????
{
????????//?Add?application?related?services?to?service?collection.
????}

????public?void?Configure(IApplicationBuilder?app,?IHostingEnvironment?env)
????
{
????????//?Create?application?pipeline.?We?will?not?focus?on?this?method.
????}
}

如果單純觀察上述代碼那么并沒(méi)有任何的稀奇之處。ConfigureServices 方法將應(yīng)用需要的類型全部添加到 IServiceCollection 實(shí)例中,而 Configure 來(lái)構(gòu)建 Pipeline(我們此次不討論該方法)。但是如果我們需要記錄日志,讀取配置文件,在應(yīng)用程序生命周期事件中注冊(cè)新的處理方法時(shí),我們可以將其直接注入 Startup 中。例如:

public?class?Startup
{
????readonly?IConfiguration?configuration;
????readonly?IApplicationLifetime?lifetime;
????readonly?ILogger<Startup>?logger;

????public?Startup(
????????IConfiguration?configuration,?IApplicationLifetime?lifetime,?ILogger<Startup>?logger
)
????
{
????????this.configuration?=?configuration;
????????this.lifetime?=?lifetime;
????????this.logger?=?logger;
????}

????public?void?ConfigureServices(IServiceCollection?services)
????
{
????????//?Add?application?related?services?to?service?collection.
????}

????public?void?Configure(IApplicationBuilder?app,?IHostingEnvironment?env)
????
{
????????//?Create?application?pipeline.
????}
}

那么問(wèn)題就來(lái)了。

  • 在 Startup 中注入的 configuration、lifetime、logger 這些服務(wù)是由哪一個(gè) IServiceProvider 創(chuàng)建出來(lái)的呢?

  • 如果在 Startup 創(chuàng)建時(shí) IServiceProvider 已然創(chuàng)建,那么 Startup.ConfigureServices 在向哪個(gè) IServiceCollection 實(shí)例添加類型呢?

  • 應(yīng)用程序運(yùn)行期間的 IServiceProvider 是在 Startup 創(chuàng)建之前就創(chuàng)建好的那個(gè)呢、還是由 Startup 配置的 IServiceCollection 實(shí)例創(chuàng)建的那個(gè)呢?

3 兩階段 ServiceProvider 創(chuàng)建

既然 Startup 中已經(jīng)有一個(gè) IServiceProvider 來(lái)給相應(yīng)的類型進(jìn)行依賴注入,而平時(shí)的應(yīng)用程序中的依賴注入又能夠包含 Startup.ConfigureServices 中的類型定義,那么說(shuō)明在整個(gè)初始化過(guò)程中先后創(chuàng)建了兩個(gè) IServiceProvider 對(duì)象。

即 ASP.NET Core 的初始化包含了兩個(gè)步驟:

  • 第一個(gè)步驟是 Hosting 相關(guān)服務(wù)的初始化過(guò)程,初始化完畢之后創(chuàng)建了第一個(gè) IServiceProvider 對(duì)象;

  • 第二步是 Application 相關(guān)服務(wù)的初始化過(guò)程。而 Application 的初始化過(guò)程可以注入 Hosting 相關(guān)的服務(wù)。之后,通過(guò) IStartup.ConfigureServices 方法創(chuàng)建了第二個(gè) IServiceProvider 對(duì)象。

如果你對(duì)源代碼感興趣

請(qǐng)參考 WebHostBuilder 類的 Build 方法(源代碼在這里:https://github.com/aspnet/AspNetCore/blob/master/src/Hosting/Hosting/src/WebHostBuilder.cs)。大致的過(guò)程如下:

  • BuildCommonServices 方法將所有 Hosting 所需的服務(wù)(WebHost 相關(guān)類型以及所有 IWebHostBuilder 調(diào)用中添加的服務(wù)類型)添加到 IServiceCollection 對(duì)象中。

  • 使用該 IServiceCollection 創(chuàng)建 Hosting 相關(guān)的 IServiceProvider,不妨稱之為 hostingServiceProvider。

  • 使用該 hostingServiceProvider 創(chuàng)建 IStartup 對(duì)象(這里有和環(huán)境相關(guān)的 Convension,詳情請(qǐng)參見(jiàn)上一篇)。

  • 使用一個(gè)復(fù)制的 IServiceCollection 對(duì)象調(diào)用 IStartup.ConfigureServices 方法創(chuàng)建另外一個(gè) IServiceProvider 不妨稱之為 applicationServiceProvider。

在了解了上述過(guò)程之后,那么我們需要注意些什么呢?

首先我們已經(jīng)了解,Startup 可以使用 Hosting 的 IServiceProvider 進(jìn)行注入。但是 IServiceProvider 是一個(gè)頂級(jí)的 Provider,如果我們?cè)?Startup 中創(chuàng)建了一個(gè)非常消耗資源的對(duì)象(實(shí)現(xiàn)了 IDisposable),則在默認(rèn)情況下該對(duì)象只有在應(yīng)用程序徹底退出的時(shí)候才會(huì)銷毀。若顯式 Dispose 該對(duì)象的話且該對(duì)象不是 Transient Scope。則有可能導(dǎo)致 Defect。

4 規(guī)避初始化過(guò)程中的資源泄露

但是如果我真的需要在初始化的時(shí)候注入非常消耗資源的對(duì)象,而我又希望規(guī)避資源的泄露,我該怎么辦呢?其實(shí)還是有辦法的。那就是不使用 Startup 的構(gòu)造函數(shù)進(jìn)行注入而是直接在 Configure 方法中通過(guò)參數(shù)進(jìn)行注入。

為什么這種方式可以規(guī)避資源泄露呢?因?yàn)檫@種注入機(jī)智并非典型的依賴注入機(jī)制,而是 ASP.NET Core 特意實(shí)現(xiàn)的。如果應(yīng)用程序在初始化時(shí)使用的 UseStartup<TStartup>() 中的 TStartup 并沒(méi)有實(shí)現(xiàn) IStartup 的話,ASP.NET Core 就會(huì)使用基于約定的 IStartup 實(shí)現(xiàn)對(duì) TStartup 進(jìn)行包裝。在包裝過(guò)程中,它會(huì)嘗試找到 TStartup 類型中的 Configure 方法,檢查參數(shù)表中的參數(shù),并使用 IStartup.ConfigureServices 創(chuàng)建的 IServiceProvider 進(jìn)行注入。但是這里的 IServiceProvider 卻并不初始化過(guò)程中的頂級(jí) Provider。而是在將整個(gè)方法調(diào)用包裹在了 Scope 里。因此即使在初始化過(guò)程中創(chuàng)建非常消耗資源的實(shí)例也會(huì)隨著方法調(diào)用結(jié)束后 Scope 的 Dispose 而銷毀。具體代碼請(qǐng)參見(jiàn):ConfigureBuilder 源代碼 (https://github.com/aspnet/AspNetCore/blob/master/src/Hosting/Hosting/src/Internal/ConfigureBuilder.cs)

5 總結(jié)

請(qǐng)飛到文章開(kāi)頭的第 0 節(jié) :-D。

如果您覺(jué)得本文對(duì)您有幫助,也歡迎分享給其他的人。我們一起進(jìn)步。歡迎關(guān)注我的博客(https://clrdaily.com)和微信公眾號(hào):


總結(jié)

以上是生活随笔為你收集整理的ASP.NET Core 沉思录 - ServiceProvider 的二度出生的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 亚洲国产黄色片 | 日产精品久久久久 | 亚洲a级在线观看 | 国产最爽的乱淫视频国语对白 | 亚洲三级大片 | 亚洲第一黄网 | 日韩激情网址 | 色秀视频网 | 精品乱人伦一区二区三区 | 一本大道伊人av久久综合 | 国产成人一区二区在线 | 国产精品卡一 | 黄色大片网站在线观看 | 欧美精品导航 | 无码视频一区二区三区 | www.爱色av.com | 国产成人在线免费 | 欧美com| 亚洲老女人视频 | 亚洲精品伦理 | 免费成人深夜在线观看 | 欧美日韩精品在线视频 | 99精品综合 | 1024手机在线观看 | 骚虎tv| 亚洲乱码精品久久久久 | 亚洲国产日韩a在线播放性色 | 不卡三区 | 男人天堂免费视频 | 在线成年人视频 | 伊人久久av | 免费在线观看av网站 | 狠狠干夜夜干 | 国产精品情侣呻吟对白视频 | 麻豆传媒一区二区 | 日本久久亚洲 | 国产午夜福利一区 | 阿拉伯性视频xxxx | 日韩黄色免费 | 狠狠爱天天干 | va欧美| 日本久久一区 | 丝袜美女av | 四虎成人网 | 国产探花在线精品一区二区 | 精品久久五月天 | 韩国三级在线看 | 欧美一区二区成人 | 国产av电影一区二区 | 激情网色 | 久久精品国产电影 | 欧美不卡一区 | 精品国产乱码一区二 | 动漫美女视频 | 美女扒开腿男人爽桶 | 成人午夜免费福利视频 | asian日本肉体pics | 你懂的视频在线播放 | 亚洲国产精品福利 | 久久国产成人精品 | 黄色一级免费网站 | 亚洲专区中文字幕 | 国产特黄 | 天天干天天天天 | 老妇裸体性猛交视频 | 久久av中文字幕 | 封神榜二在线高清免费观看 | 大肉大捧一进一出好爽 | 97国产成人无码精品久久久 | 亚洲狼人色 | 色综合天天射 | 精品国产黄 | 精品在线播放视频 | 久久久久久97 | 少妇高潮一区二区三区四区 | 精品久久久久久久久久久久久久久 | 国产一区二区91 | 99国产视频 | 亚洲综合在线视频 | 一级黄色在线观看 | 久久在线免费观看 | 自拍超碰 | 国产色在线观看 | 日本道在线观看 | 精品无码久久久久成人漫画 | av调教 | 内射毛片内射国产夫妻 | 麻豆精品国产传媒mv男同 | 国产一av | 国产精品久久久久久亚洲调教 | 中文字幕日韩三级片 | 999久久久免费精品国产 | 蜜桃视频久久一区免费观看入口 | 日韩一区二区三区不卡 | 午夜激情成人 | 亚洲国产综合在线 | 老妇女av | 中文字幕首页 | 婷婷综合色 |