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

歡迎訪問 生活随笔!

生活随笔

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

asp.net

ASP.NET Core中如影随形的”依赖注入”[上]: 从两个不同的ServiceProvider说起

發(fā)布時間:2024/4/17 asp.net 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ASP.NET Core中如影随形的”依赖注入”[上]: 从两个不同的ServiceProvider说起 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

我們一致在說 ASP.NET Core廣泛地使用到了依賴注入,通過前面兩個系列的介紹,相信讀者朋友已經(jīng)體會到了這一點。由于前面兩章已經(jīng)涵蓋了依賴注入在管道構(gòu)建過程中以及管道在處理請求過程的應(yīng)用,但是內(nèi)容相對分散和零碎,我們有必要針對這個主題作一個歸納性的介紹。采用依賴注入的服務(wù)均由某個ServiceProvider來提供,但是在ASP.NET Core管道涉及到兩個不同的ServiceProvider,其中一個是在管道成功構(gòu)建后創(chuàng)建并綁定到WebHost上的ServiceProvider,對應(yīng)著WebHost的Services屬性。另一個ServiceProvider則是在管道處理每個請求時即時創(chuàng)建的,它綁定當(dāng)表示當(dāng)前請求上下文上,對應(yīng)著HttpContext的RequestServices屬性,兩個ServiceProvider之間存在著父子關(guān)系。[本文已經(jīng)同步到《ASP.NET Core框架揭秘》之中]

目錄
一、WebHost的ServiceProvider
二、HttpContext的ServiceProvider
??? 原理分析
??? 實例證明
??? 兩個ServiceProvider具有“父子”關(guān)系
??? ServiceProvidersFeature特性
??? RequestServicesContainerMiddleware中間件
??? AutoRequestServicesStartupFilter

一、WebHost的ServiceProvider

ASP.NET Core的依賴注入框架其實很簡單,其中僅僅涉及ServiceCollection和ServiceProvider這兩個核心對象。我們預(yù)先將服務(wù)描述信息注冊到ServiceCollection之上,然后利用ServiceCollection來創(chuàng)建ServiceProvider,并最終利用后者根據(jù)指定的服務(wù)類型來提供對應(yīng)的服務(wù)實例。接下來我們以這兩個對象作為唯一的關(guān)注點來回顧一下管道的創(chuàng)建流程。ASP.NET Core管道的創(chuàng)建也僅僅涉及到兩個核心對象,作為應(yīng)用宿主的WebHost對象和創(chuàng)建它的WebHostBuilder。下圖基本揭示了WebHostBuilder創(chuàng)建WebHost,以及WebHost在開啟過程針對依賴注入這兩個核心對象的使用。

ASP.NET Core管道在構(gòu)建過程中會使用同一個ServiceCollection,所有注冊的服務(wù)都被添加到這個對象上。這個ServiceCollection對象最初由WebHostBuilder創(chuàng)建。在WebHost的創(chuàng)建過程中,WebHostBuilder需要向這個ServiceCollection對象注冊兩種類型的服務(wù):一種是確保管道能夠被成功構(gòu)建并順利處理請求所必需的服務(wù),我們不妨將它們稱為系統(tǒng)服務(wù);另一種則是用戶通過調(diào)用ConfigureServices方法自行注冊的服務(wù),我們姑且稱它們?yōu)?font color="#ff0000">用戶服務(wù)。

當(dāng)上述這兩種服務(wù)被成功注冊之后,WebHostBuilder會利用這個ServiceCollection創(chuàng)建一個ServiceProvider對象,這個對象和ServiceCollection將一并遞交給由它創(chuàng)建的WebHost對象。當(dāng)WebHost在初始化過程中,它的第一項過程就是利用ServiceProvider獲取一個Startup對象。如果這一個ConventionBasedStartup對象是,并且對應(yīng)的啟動類是一個實例類,具體的啟動對象是采用依賴注入的形式被實例化的,所以啟動類的構(gòu)造函數(shù)是可以有參數(shù)的。啟動對象實例化過程中使用的就是WebHostBuilder提供的這個ServiceProvider,這也是依賴注入的第一次應(yīng)用。

當(dāng)WebHost利用WebHostBuilder提供的這個ServiceProvider得到這個Startup對象之后,它會調(diào)用其ConfigureServices方法將用戶在啟動類中注冊的服務(wù)添加到上述這個ServiceCollection對象之上,到目前為止這個ServiceCollection包含了所有需要注冊的服務(wù)。如果啟動類型的ConfigureServices方法沒有返回值,那么這個ServiceCollection將被用來創(chuàng)建一個新的ServiceProvider,后續(xù)過程中所有的服務(wù)都會利用它來獲取。如果啟動類型的ConfigureServices方法返回一個ServiceProvider,那么后續(xù)過程作為服務(wù)提供者的就是這么一個對象。WebHost的Services屬性返回的就是這個ServiceProvider對象,所以姑且稱它為WebHost的ServiceProvider。

接下來WebHost利用這個ServiceProvider獲取注冊的ApplicationBuilder對象和StartupFilter對象,并將前者作為參數(shù)依次調(diào)用每個StartupFilter的Configure方法進(jìn)行中間件的注冊。當(dāng)針對所有StartupFilter的調(diào)用都結(jié)束之后,WebHost才會選擇調(diào)用Startup對象的Configure方法。對于通過這兩種形式注冊的中間件,如果對應(yīng)的是一個遵循約定的中間件類型的話,WebHost同樣會采用依賴注入的方式來實例化中間件對象,所以中間件類型的構(gòu)造函數(shù)也是可以有參數(shù)的,這是對依賴注入的第二次應(yīng)用。

到所有中間件都被注冊之后,WebHost會調(diào)用ApplicationBuilder的Build方法生成一個RequestDelegate對象,這個對象體現(xiàn)了所有中間件組成一個有序鏈表。接下來,WebHost利用這個RequestDelegate對象創(chuàng)建一個HttpApplication對象(默認(rèn)創(chuàng)建的是一個HostingHttpApplication對象)。隨后,WebHost利用ServiceProvider提取出最初注冊在WebHostBuilder上的服務(wù)器,并將HttpApplication對象作為參數(shù)調(diào)用其Start方法啟動該服務(wù)器。從此,這個以服務(wù)器和注冊中間件構(gòu)成的管道被成功創(chuàng)建出來,服務(wù)器隨之開始綁定到指定的監(jiān)聽地址監(jiān)聽來自網(wǎng)絡(luò)的請求。

二、HttpContext的ServiceProvider

請求一旦抵達(dá)并被服務(wù)器接收,服務(wù)器會將它將給后邊的中間件執(zhí)行。如果中間件對應(yīng)的是一個按照約定對應(yīng)的中間件類型,對請求的處理體現(xiàn)在對它的Invoke方法的執(zhí)行。針對中間件類型Invoke方法的執(zhí)行同樣采用了依賴注入的形式來提供該方法從第二開始的所有參數(shù),這是對依賴注入的第三次應(yīng)用。那么現(xiàn)在問題來了,針對每次請求所使用的ServiceProvider依然是WebHost的ServiceProvider嗎?如果不是 ,那么兩者是什么關(guān)系?

原理分析

我們先來回答第一個問題。對于某個由ServiceProvider提供的服務(wù)對象說,針對它的回收也是由這個ServiceProvider來完成的。具體來說,非根ServiceProvider在自身被回收的時候,由它提供的采用Scoped和Transient模式的服務(wù)實例會自動被回收;至于采用Singleton模式的服務(wù)實例,針對它們的回收發(fā)生在跟ServiceProvider自身被回收的時候。

如果我們在這個ServiceProvider上以Transient模式注冊了一個服務(wù),這意味著每次從ServiceProvider提取的都是一個全新的對象。如果這些對象引用著一些需要被回收的資源,我們希望資源的回收應(yīng)該在每次請求處理結(jié)束之后自動執(zhí)行。如果管道每次處理請求時所使用的都是同一個ServiceProvider對象,那么針對服務(wù)實例的回收只能在整個應(yīng)用終止的時候才會發(fā)生,這無疑會產(chǎn)生內(nèi)存泄漏的問題。基于這個原因。管道總是會創(chuàng)建一個新的ServiceProvider來提供處理每個請求所需的服務(wù),并且這個ServiceProvider將在每次請求處理完成之后被自動回收掉。這樣一個ServiceProvider被創(chuàng)建之后直接保存到當(dāng)前的HTTP上下文中,我們可以利用HttpContext如下所示的RequestServices屬性得到這個ServiceProvider。

1: public?abstract?class HttpContext 2: { 3:???? public?abstract IServiceProvider RequestServices { get; set; } 4:??? ... 5: }

實例證明

我們上面僅僅從理論層面解釋了為什么針對每次請求所使用的ServiceProvider都不相同,接下來我們可以通過實例演示的方式來證實這個推論是成立的。我們在一個控制臺應(yīng)用中編寫了如下的代碼來啟動一個ASP.NET Core應(yīng)用。我們以不同的生命周期模式(Singleton、Scoped和Transient)之注冊三個服務(wù),具體的服務(wù)類型都實現(xiàn)了IDisposable接口,而實現(xiàn)的Dispose方法會在控制臺上打印相應(yīng)的文字指示那個類型的Dispose方法被執(zhí)行了。通過調(diào)用Configure方法注冊的中間件會利用從當(dāng)前HttpContext獲取的ServiceProvider來提供三個對象的服務(wù)對象。

1: public?class Program 2: { 3:???? public?static?void Main() 4:???? { 5:???????? new WebHostBuilder() 6:???????????? .ConfigureLogging(loggerFactory=>loggerFactory.AddConsole()) 7:???????????? .UseKestrel() 8:???????????? .ConfigureServices(svcs=>svcs 9:???????????????? .AddSingleton<IFoo, Foo>() 10:???????????????? .AddScoped<IBar, Bar>() 11:???????????????? .AddTransient<IBaz, Baz>()) 12:???????????? .Configure(app => app.Run(async context =>{ 13:???????????????? context.RequestServices.GetService<IFoo>(); 14:???????????????? context.RequestServices.GetService<IBar>(); 15:???????????????? context.RequestServices.GetService<IBaz>(); 16:???????????????? await context.Response.WriteAsync("End"); 17:???????????? })) 18:???????????? .Build() 19:???????????? .Run(); 20:???? } 21: } 22:? 23: public?interface IFoo {} 24: public?interface IBar {} 25: public?interface IBaz {} 26: public?class ServiceBase : IDisposable 27: { 28:???? public?void Dispose() 29:???? { 30:???????? Console.WriteLine($"{this.GetType().Name}.Dispose()..."); 31:???? } 32: } 33: public?class Foo : ServiceBase, IFoo {} 34: public?class Bar : ServiceBase, IBar {} 35: public?class Baz : ServiceBase, IBaz {}

由于我們調(diào)用 WebHostBuilder的ConfigureLogging方法添加了ConsoleLoggerProvider,所以管道在開始和結(jié)束請求的時候會在當(dāng)前控制臺上寫入相應(yīng)的日志。啟動應(yīng)用之后,我們利用瀏覽器向默認(rèn)的監(jiān)聽地址連續(xù)發(fā)送兩次請求后,控制臺上將會產(chǎn)生如下所示的輸出結(jié)果。這樣的輸出結(jié)果表明:對于當(dāng)前請求處理過程中獲取的非Sington服務(wù)對象都會請求處理結(jié)束之后被自動回收。

1: info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] 2:?????? Request starting HTTP/1.1 GET http://localhost:5000/ 3: 4: 5: info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] 6:?????? Request finished in 74.9439ms 200 7:? 8: info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] 9:?????? Request starting HTTP/1.1 GET http://localhost:5000/ 10: 11: 12: info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] 13:?????? Request finished in 0.8272ms 200

兩個ServiceProvider具有“父子”關(guān)系

回到前面提到的第二個問題,處理每個請求創(chuàng)建的ServiceProvider和管道構(gòu)建成功時創(chuàng)建的ServiceProvider(對應(yīng)WebHost的Services屬性)之間具有怎樣的關(guān)系,其實兩者之間的關(guān)系很簡單,是“父子”關(guān)系。下圖不僅僅體現(xiàn)了這兩種類型的ServiceProvider各自具有的生命周期,同時也體現(xiàn)了它們之間的關(guān)系。WebHost的生命周期也就是整個應(yīng)用的生命周期,所以WebHost的Services屬性返回的ServiceProvider是一個全局單例對象。當(dāng)WebHost隨著其Dispose方法被調(diào)用而被關(guān)閉時,它會調(diào)用ServiceProvider的Dispose方法。

ASP.NET Core管道針對每個請求的處理都在一個全新的HTTP上下文(HttpContext)中進(jìn)行,提供請求處理所需服務(wù)的ServiceProvider與當(dāng)前上下文綁定在一起,通過HttpContext對象的RequestServices屬性返回。由于這個ServiceProvider將WebHost的ServiceProvider作為“父親” ,所以之前添加的所有服務(wù)注冊對于它來說依然有效。當(dāng)前請求一旦結(jié)束,當(dāng)前HttpContext自然 “壽終正寢” ,與之關(guān)聯(lián)的ServiceProvider也隨之被回收釋放。

ServiceProvidersFeature特性

在了解了兩種類型的ServiceProvider各種具有的生命周期和相互關(guān)系之后,我們需要了解這個為請求處理提供服務(wù)的ServiceProvider是如何被創(chuàng)建,又是如何被回收釋放的。對作為默認(rèn)HttpContext的DefaultHttpContext對象來說,它的RequestServices屬性返回的ServiceProvider來源于一個名為ServiceProvidersFeature的特性。所謂的ServiceProvidersFeature特性是對所有實現(xiàn)了IServiceProvidersFeature接口的類型以及對應(yīng)對象的統(tǒng)稱。如下面的代碼片段所示,這個接口具有一個唯一屬性RequestServices正好用于返回和設(shè)置這個ServiceProvider。

1: public?interface IServiceProvidersFeature 2: { 3:???? IServiceProvider RequestServices { get; set; } 4: }

ASP.NET Core默認(rèn)使用的ServiceProvidersFeature是一個類型為RequestServicesFeature的對象,如下所示的代碼片段體現(xiàn)了它提供ServiceProvider的邏輯。在創(chuàng)建一個RequestServicesFeature對象的時候,我們需要提供一個根據(jù)某個ServiceProvider創(chuàng)建 ServiceScopeFactory對象,它所提供的ServiceProvider就是根據(jù)這個ServiceScopeFactory提供的ServiceScope對象創(chuàng)建的。我們根據(jù)根據(jù)提供的代碼可知針對這個屬性的多次調(diào)用返回的實際上是同一個ServiceProvider。RequestServicesFeature還是實現(xiàn)IDisposable接口,并在實現(xiàn)的Dispose放過中釋放了這個ServiceScope,我們知道此舉實際上是為了實現(xiàn)對提供的這個ServiceProvider實施回收。

1: public?class RequestServicesFeature : IServiceProvidersFeature, IDisposable 2: { 3:???? private IServiceScopeFactory???? _scopeFactory; 4:???? private IServiceProvider???????? _requestServices; 5:???? private IServiceScope??????????? _scope; 6:???? private?bool???????????????????? _requestServicesSet; 7:? 8:???? public RequestServicesFeature(IServiceScopeFactory scopeFactory) 9:???? { 10:???????? _scopeFactory = scopeFactory; 11:???? } 12:? 13:???? public IServiceProvider RequestServices 14:???? { 15:???????? get 16:???????? { 17:???????????? if (!_requestServicesSet) 18:???????????? { 19:???????????????? _scope = _scopeFactory.CreateScope(); 20:???????????????? _requestServices = _scope.ServiceProvider; 21:???????????????? _requestServicesSet = true; 22:???????????? } 23:???????????? return _requestServices; 24:???????? } 25:? 26:???????? set 27:???????? { 28:???????????? _requestServices = value; 29:???????????? _requestServicesSet = true; 30:???????? } 31:???? } 32:? 33:???? public?void Dispose() 34:???? { 35:???????? _scope?.Dispose(); 36:???????? _scope = null; 37:???????? _requestServices = null; 38:???? } 39: }

RequestServicesContainerMiddleware中間件

那么這個RequestServicesFeature特性又是如何被添加到當(dāng)前HttpContext的特性集合中的呢?這實際上又涉及到一個名為RequestServicesContainerMiddleware的中間件。我們在創(chuàng)建這個中間件的時候需要提供一個ServiceScopeFactory,該中間件會在Invoke方法被執(zhí)行的時候根據(jù)它創(chuàng)建一個RequestServicesFeature對象,并將其添加到當(dāng)前HttpContext的特性集合中。當(dāng)后續(xù)的請求處理結(jié)束之后,添加的這個RequestServicesFeature對象會被回收釋放,并從HttpContext的特性集合中去除。實際上HttpContext的RequestServices返回的ServiceProvider就是在這里被回收釋放的。

1: public?class RequestServicesContainerMiddleware 2: { 3:???? private?readonly RequestDelegate???? _next; 4:???? private IServiceScopeFactory???????? _scopeFactory; 5:? 6:???? public RequestServicesContainerMiddleware(RequestDelegate next, IServiceScopeFactory scopeFactory) 7:???? {??????? 8:???????? _scopeFactory???? = scopeFactory; 9:???????? _next???????????? = next; 10:???? } 11:? 12:???? public async Task Invoke(HttpContext httpContext) 13:???? {?????????? 14:? 15:???????? var existingFeature = httpContext.Features.Get<IServiceProvidersFeature>(); 16:???????? if (existingFeature?.RequestServices != null) 17:???????? { 18:???????????? await _next.Invoke(httpContext); 19:???????????? return; 20:???????? } 21:? 22:???????? using (var feature = new RequestServicesFeature(_scopeFactory)) 23:???????? { 24:???????????? try 25:???????????? { 26:???????????????? httpContext.Features.Set<IServiceProvidersFeature>(feature); 27:???????????????? await _next.Invoke(httpContext); 28:???????????? } 29:???????????? finally 30:???????????? { 31:???????????????? httpContext.Features.Set(existingFeature); 32:???????????? } 33:???????? } 34:???? } 35: }

AutoRequestServicesStartupFilter

RequestServicesContainerMiddleware中間件的注冊最終通過一個StartupFilter對象來完成的,它的類型就是具有如下定義的AutoRequestServicesStartupFilter。對于其Configure方法返回的這個Action<IApplicationBuilder>對象來說,它在注冊這個中間件的時候并沒有明確之定義一個具體的ServiceScopeFactory對象,那么毫無疑問該中間件使用的ServiceScopeFactory就是根據(jù)WebHost的ServiceProvider提供的。WebHost的ServiceProvider提供了一個ServiceScopeFactory,而HttpContext的ServiceProvider又是根據(jù)這個ServiceScopeFactory提供的ServiceScope創(chuàng)建的,這兩個ServiceProvider之間的父子關(guān)系就是采用形式確立的。

1: public?class AutoRequestServicesStartupFilter : IStartupFilter 2: { 3:???? public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next) 4:???? { 5:???????? return app => 6:???????? { 7:???????????? app.UseMiddleware<RequestServicesContainerMiddleware>(); 8:???????????? next(app); 9:???????? }; 10:???? } 11: }

在WebHostBuilder創(chuàng)建WebHost之前,它會注冊一系列確保后續(xù)的管道能夠正常構(gòu)建并處理請求所必須的服務(wù),這其中就包括這個AutoRequestServicesStartupFilter。綜上所述,通過HttpContext的RequestServices屬性返回的一個用于提供請求處理過程所需服務(wù)的ServiceProvider,這個ServiceProvider的創(chuàng)建和回收釋放按是通過一個特性(RequestServicesFeature)、一個中間件(RequestServicesContainerMiddleware)和一個StartupFilter(AutoRequestServicesStartupFilter)相互協(xié)作完成的。

我們知道注冊服務(wù)具有三種生命周期模式(Singleton、Scoped和Transient)。由于為請求處理提供所需服務(wù)的ServiceProvider是基于當(dāng)前請求上下文的,所以這三種生命周期模式在ASP.NET Core應(yīng)用中體現(xiàn)了服務(wù)實例的復(fù)用等級。具體來說,Singleton服務(wù)在整個應(yīng)用生命周期中復(fù)用,Scoped服務(wù)僅在當(dāng)前請求上下文中復(fù)用,而Transient服務(wù)則不能被復(fù)用,

轉(zhuǎn)載于:https://www.cnblogs.com/artech/p/di-asp-net-core-pipeline-1.html

總結(jié)

以上是生活随笔為你收集整理的ASP.NET Core中如影随形的”依赖注入”[上]: 从两个不同的ServiceProvider说起的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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