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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

ASP.NET Core中的依赖注入(4): 构造函数的选择与服务生命周期管理

發布時間:2023/12/6 asp.net 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ASP.NET Core中的依赖注入(4): 构造函数的选择与服务生命周期管理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

ServiceProvider最終提供的服務實例都是根據對應的ServiceDescriptor創建的,對于一個具體的ServiceDescriptor對象來說,如果它的ImplementationInstance和ImplementationFactory屬性均為Null,那么ServiceProvider最終會利用其ImplementationType屬性返回的真實類型選擇一個適合的構造函數來創建最終的服務實例。我們知道服務服務的真實類型可以定義了多個構造函數,那么ServiceProvider針對構造函數的選擇會采用怎樣的策略呢?

目錄
一、構造函數的選擇
二、生命周期管理
??? ServiceScope與ServiceScopeFactory
??? 三種生命周期管理模式
??? 服務實例的回收

一、構造函數的選擇

如果ServiceProvider試圖通過調用構造函數的方式來創建服務實例,傳入構造函數的所有參數必須先被初始化,最終被選擇出來的構造函數必須具備一個基本的條件:ServiceProvider能夠提供構造函數的所有參數。為了讓讀者朋友能夠更加真切地理解ServiceProvider在構造函數選擇過程中采用的策略,我們不讓也采用實例演示的方式來進行講解。

我們在一個控制臺應用中定義了四個服務接口(IFoo、IBar、IBaz和IGux)以及實現它們的四個服務類(Foo、Bar、Baz和Gux)。如下面的代碼片段所示,我們為Gux定義了三個構造函數,參數均為我們定義了服務接口類型。為了確定ServiceProvider最終選擇哪個構造函數來創建目標服務實例,我們在構造函數執行時在控制臺上輸出相應的指示性文字。

1: public?interface IFoo {} 2: public?interface IBar {} 3: public?interface IBaz {} 4: public?interface IGux {} 5:? 6: public?class Foo : IFoo {} 7: public?class Bar : IBar {} 8: public?class Baz : IBaz {} 9: public?class Gux : IGux 10: { 11:???? public Gux(IFoo foo) 12:???? { 13:???????? Console.WriteLine("Gux(IFoo)"); 14:???? } 15:? 16:???? public Gux(IFoo foo, IBar bar) 17:???? { 18:???????? Console.WriteLine("Gux(IFoo, IBar)"); 19:???? } 20:? 21:???? public Gux(IFoo foo, IBar bar, IBaz baz) 22:???? { 23:???????? Console.WriteLine("Gux(IFoo, IBar, IBaz)"); 24:???? } 25: }

我們在作為程序入口的Main方法中創建一個ServiceCollection對象并在其中添加針對IFoo、IBar以及IGux這三個服務接口的服務注冊,針對服務接口IBaz的注冊并未被添加。我們利用由它創建的ServiceProvider來提供針對服務接口IGux的實例,究竟能否得到一個Gux對象呢?如果可以,它又是通過執行哪個構造函數創建的呢?

1: class Program 2: { 3:???? static?void Main(string[] args) 4:???? {?????? 5:???????? new ServiceCollection() 6:???????????? .AddTransient<IFoo, Foo>() 7:???????????? .AddTransient<IBar, Bar>() 8:???????????? .AddTransient<IGux, Gux>() 9:???????????? .BuildServiceProvider() 10:???????????? .GetServices<IGux>(); 11:???? } 12: }

對于定義在Gux中的三個構造函數來說,ServiceProvider所在的ServiceCollection包含針對接口IFoo和IBar的服務注冊,所以它能夠提供前面兩個構造函數的所有參數。由于第三個構造函數具有一個類型為IBaz的參數,這無法通過ServiceProvider來提供。根據我們上面介紹的第一個原則(ServiceProvider能夠提供構造函數的所有參數),Gux的前兩個構造函數會成為合法的候選構造函數,那么ServiceProvider最終會選擇哪一個呢?

在所有合法的候選構造函數列表中,最終被選擇出來的構造函數具有這么一個特征:每一個候選構造函數的參數類型集合都是這個構造函數參數類型集合的子集。如果這樣的構造函數并不存在,一個類型為InvalidOperationException的異常會被跑出來。根據這個原則,Gux的第二個構造函數的參數類型包括IFoo和IBar,而第一個構造函數僅僅具有一個類型為IFoo的參數,最終被選擇出來的會是Gux的第二個構造函數,所有運行我們的實例程序將會在控制臺上產生如下的輸出結果。

1: Gux(IFoo, IBar)

接下來我們對實例程序略加改動。如下面的代碼片段所示,我們只為Gux定義兩個構造函數,它們都具有兩個參數,參數類型分別為IFoo&IBar和IBar&IBaz。在Main方法中,我們將針對IBaz/Baz的服務注冊添加到創建的ServiceCollection上。

1: class Program 2: { 3:???? static?void Main(string[] args) 4:???? {?????? 5:???????? new ServiceCollection() 6:???????????? .AddTransient<IFoo, Foo>() 7:???????????? .AddTransient<IBar, Bar>() 8:???????????? .AddTransient<IBaz, Baz>() 9:???????????? .AddTransient<IGux, Gux>() 10:???????????? .BuildServiceProvider() 11:???????????? .GetServices<IGux>(); 12:???? } 13: } 14:? 15: public?class Gux : IGux 16: { 17:???? public Gux(IFoo foo, IBar bar) {} 18:???? public Gux(IBar bar, IBaz baz) {} 19: }

對于Gux的兩個構造函數,雖然它們的參數均能夠由ServiceProvider來提供,但是并沒有一個構造函數的參數類型集合能夠成為所有有效構造函數參數類型集合的超集,所以ServiceProvider無法選擇出一個最佳的構造函數。如果我們運行這個程序,一個InvalidOperationException異常會被拋出來,控制臺上將呈現出如下所示的錯誤消息。

1: Unhandled Exception: System.InvalidOperationException: Unable to activate type 'Gux'. The following constructors are ambigious: 2: Void .ctor(IFoo, IBar) 3: Void .ctor(IBar, IBaz) 4: ...

二、生命周期管理

生命周期管理決定了ServiceProvider采用怎樣的方式創建和回收服務實例。ServiceProvider具有三種基本的生命周期管理模式,分別對應著枚舉類型ServiceLifetime的三個選項(Singleton、Scoped和Transient)。對于ServiceProvider支持的這三種生命周期管理模式,Singleton和Transient的語義很明確,前者(Singleton)表示以“單例”的方式管理服務實例的生命周期,意味著ServiceProvider對象多次針對同一個服務類型所提供的服務實例實際上是同一個對象;而后者(Transient)則完全相反,對于每次服務提供請求,ServiceProvider總會創建一個新的對象。那么Scoped又體現了ServiceProvider針對服務實例怎樣的生命周期管理方式呢?

ServiceScope與ServiceScopeFactory

ServiceScope為某個ServiceProvider對象圈定了一個“作用域”,枚舉類型ServiceLifetime中的Scoped選項指的就是這么一個ServiceScope。在依賴注入的應用編程接口中,ServiceScope通過一個名為IServiceScope的接口來表示。如下面的代碼片段所示,繼承自IDisposable接口的IServiceScope具有一個唯一的只讀屬性ServiceProvider返回確定這個服務范圍邊界的ServiceProvider。表示ServiceScope由它對應的工廠ServiceScopeFactory來創建,后者體現為具有如下定義的接口IServiceScopeFactory。

1: public?interface IServiceScope : IDisposable 2: { 3:???? IServiceProvider ServiceProvider { get; } 4: } 5:? 6: public?interface IServiceScopeFactory 7: { 8:???? IServiceScope CreateScope(); 9: }

若要充分理解ServiceScope和ServiceProvider之間的關系,我們需要簡單了解一下ServiceProvider的層級結構。除了直接通過一個ServiceCollection對象創建一個獨立的ServiceProvider對象之外,一個ServiceProvider還可以根據另一個ServiceProvider對象來創建,如果采用后一種創建方式,我們指定的ServiceProvider與創建的ServiceProvider將成為一種“父子”關系。

1: internal?class ServiceProvider : IServiceProvider, IDisposable 2: { 3:???? private?readonly ServiceProvider _root; 4:???? internal ServiceProvider(ServiceProvider parent) 5:???? { 6:???????? _root = parent._root; 7:???? } 8:???? //其他成員 9: }

雖然在ServiceProvider在創建過程中體現了ServiceProvider之間存在著一種樹形化的層級結構,但是ServiceProvider對象本身并沒有一個指向“父親”的引用,它僅僅會保留針對根節點的引用。如上面的代碼片段所示,針對根節點的引用體現為ServiceProvider類的字段_root。當我們根據作為“父親”的ServiceProvider創建一個新的ServiceProvider的時候,父子均指向同一個“根”。我們可以將創建過程中體現的層級化關系稱為“邏輯關系”,而將ServiceProvider對象自身的引用關系稱為“物理關系”,右圖清楚地揭示了這兩種關系之間的轉化。

由于ServiceProvider自身是一個內部類型,我們不能采用調用構造函數的方式根據一個作為“父親”的ServiceProvider創建另一個作為“兒子”的ServiceProvider,但是這個目的可以間接地通過創建ServiceScope的方式來完成。如下面的代碼片段所示,我們首先創建一個獨立的ServiceProvider并調用其GetService<T>方法獲得一個ServiceScopeFactory對象,然后調用后者的CreateScope方法創建一個新的ServiceScope,它的ServiceProvider就是前者的“兒子”。

1: class Program 2: { 3:???? static?void Main(string[] args) 4:???? { 5:???????? IServiceProvider serviceProvider1 = new ServiceCollection().BuildServiceProvider(); 6:???????? IServiceProvider serviceProvider2 = serviceProvider1.GetService<IServiceScopeFactory>().CreateScope().ServiceProvider; 7:? 8:???????? object root = serviceProvider2.GetType().GetField("_root", BindingFlags.Instance| BindingFlags.NonPublic).GetValue(serviceProvider2); 9:???????? Debug.Assert(object.ReferenceEquals(serviceProvider1, root));??????? 10:???? } 11: }

如果讀者朋友們希望進一步了解ServiceScope的創建以及它和ServiceProvider之間的關系,我們不妨先來看看作為IServiceScope接口默認實現的內部類型ServiceScope的定義。如下面的代碼片段所示,ServiceScope僅僅是對一個ServiceProvider對象的簡單封裝而已。值得一提的是,當ServiceScope的Dispose方法被調用的時候,這個被封裝的ServiceProvider的同名方法同時被執行。

1: { 2:???? private?readonly ServiceProvider _scopedProvider; 3:???? public ServiceScope(ServiceProvider scopedProvider) 4:???? { 5:???????? this._scopedProvider = scopedProvider; 6:???? } 7:? 8:???? public?void Dispose() 9:???? { 10:???????? _scopedProvider.Dispose(); 11:???? } 12:? 13:???? public IServiceProvider ServiceProvider 14:???? { 15:???????? get {return _scopedProvider; } 16:???? } 17: }

IServiceScopeFactory接口的默認實現類型是一個名為ServiceScopeFactory的內部類型。如下面的代碼片段所示,ServiceScopeFactory的只讀字段“_provider”表示當前的ServiceProvider。當CreateScope方法被調用的時候,這個ServiceProvider的“子ServiceProvider”被創建出來,并被封裝成返回的ServiceScope對象。

1: internal?class ServiceScopeFactory : IServiceScopeFactory 2: { 3:???? private?readonly ServiceProvider _provider; 4:???? public ServiceScopeFactory(ServiceProvider provider) 5:???? { 6:???????? _provider = provider; 7:???? } 8:? 9:???? public IServiceScope CreateScope() 10:???? { 11:???????? return?new ServiceScope(new ServiceProvider(_provider)); 12:???? } 13: }

三種生命周期管理模式

只有在充分了解ServiceScope的創建過程以及它與ServiceProvider之間的關系之后,我們才會對ServiceProvider支持的三種生命周期管理模式(Singleton、Scope和Transient)具有深刻的認識。就服務實例的提供方式來說,它們之間具有如下的差異:

  • Singleton:ServiceProvider創建的服務實例保存在作為根節點的ServiceProvider上,所有具有同一根節點的所有ServiceProvider提供的服務實例均是同一個對象。
  • Scoped:ServiceProvider創建的服務實例由自己保存,所以同一個ServiceProvider對象提供的服務實例均是同一個對象。
  • Transient:針對每一次服務提供請求,ServiceProvider總是創建一個新的服務實例。

為了讓讀者朋友們對ServiceProvider支持的這三種不同的生命周期管理模式具有更加深刻的理解,我們照例來做一個簡單的實例演示。我們在一個控制臺應用中定義了如下三個服務接口(IFoo、IBar和IBaz)以及分別實現它們的三個服務類(Foo、Bar和Baz)。

1: public?interface IFoo {} 2: public?interface IBar {} 3: public?interface IBaz {} 4:? 5: public?class Foo : IFoo {} 6: public?class Bar : IBar {} 7: public?class Baz : IBaz {}

現在我們在作為程序入口的Main方法中創建了一個ServiceCollection對象,并采用不同的生命周期管理模式完成了針對三個服務接口的注冊(IFoo/Foo、IBar/Bar和IBaz/Baz分別Transient、Scoped和Singleton)。我們接下來針對這個ServiceCollection對象創建了一個ServiceProvider(root),并采用創建ServiceScope的方式創建了它的兩個“子ServiceProvider”(child1和child2)。

1: class Program 2: { 3:???? static?void Main(string[] args) 4:???? { 5:???????? IServiceProvider root = new ServiceCollection() 6:???????????? .AddTransient<IFoo, Foo>() 7:???????????? .AddScoped<IBar, Bar>() 8:???????????? .AddSingleton<IBaz, Baz>() 9:???????????? .BuildServiceProvider(); 10:???????? IServiceProvider child1 = root.GetService<IServiceScopeFactory>().CreateScope().ServiceProvider; 11:???????? IServiceProvider child2 = root.GetService<IServiceScopeFactory>().CreateScope().ServiceProvider; 12:? 13:???????? Console.WriteLine("ReferenceEquals(root.GetService<IFoo>(), root.GetService<IFoo>() = {0}",ReferenceEquals(root.GetService<IFoo>(), root.GetService<IFoo>())); 14:???????? Console.WriteLine("ReferenceEquals(child1.GetService<IBar>(), child1.GetService<IBar>() = {0}",ReferenceEquals(child1.GetService<IBar>(), child1.GetService<IBar>())); 15:???????? Console.WriteLine("ReferenceEquals(child1.GetService<IBar>(), child2.GetService<IBar>() = {0}",ReferenceEquals(child1.GetService<IBar>(), child2.GetService<IBar>())); 16:???????? Console.WriteLine("ReferenceEquals(child1.GetService<IBaz>(), child2.GetService<IBaz>() = {0}",ReferenceEquals(child1.GetService<IBaz>(), child2.GetService<IBaz>())); 17:???? } 18: }

為了驗證ServiceProvider針對Transient模式是否總是創建新的服務實例,我們利用同一個ServiceProvider(root)獲取針對服務接口IFoo的實例并進行比較。為了驗證ServiceProvider針對Scope模式是否僅僅在當前ServiceScope下具有“單例”的特性,我們先后比較了同一個ServiceProvider(child1)和不同ServiceProvider(child1和child2)兩次針對服務接口IBar獲取的實例。為了驗證具有“同根”的所有ServiceProvider針對Singleton模式總是返回同一個服務實例,我們比較了兩個不同child1和child2兩次針對服務接口IBaz獲取的服務實例。如下所示的輸出結構印證了我們上面的論述。

1: ReferenceEquals(root.GetService<IFoo>(), root.GetService<IFoo>()???????? = False 2: ReferenceEquals(child1.GetService<IBar>(), child1.GetService<IBar>()???? = True 3: ReferenceEquals(child1.GetService<IBar>(), child2.GetService<IBar>()???? = False 4: ReferenceEquals(child1.GetService<IBaz>(), child2.GetService<IBaz>()???? = True

服務實例的回收

ServiceProvider除了為我們提供所需的服務實例之外,對于由它提供的服務實例,它還肩負起回收之責。這里所說的回收與.NET自身的垃圾回收機制無關,僅僅針對于自身類型實現了IDisposable接口的服務實例,所謂的回收僅僅體現為調用它們的Dispose方法。ServiceProvider針對服務實例所采用的收受策略取決于服務注冊時采用的生命周期管理模式,具體采用的服務回收策略主要體現為如下兩點:

  • 如果注冊的服務采用Singleton模式,由某個ServiceProvider提供的服務實例的回收工作由作為根的ServiceProvider負責,后者的Dispose方法被調用的時候,這些服務實例的Dispose方法會自動執行。
  • 如果注冊的服務采用其他模式(Scope或者Transient),ServiceProvider自行承擔由它提供的服務實例的回收工作,當它的Dispose方法被調用的時候,這些服務實例的Dispose方法會自動執行。

我們照例使用一個簡單的實例來演示ServiceProvider針對不同生命周期管理模式所采用的服務回收策略。我們在一個控制臺應用中定義了如下三個服務接口(IFoo、IBar和IBaz)以及三個實現它們的服務類(Foo、Bar和Baz),這些類型具有相同的基類Disposable。Disposable實現了IDisposable接口,我們在Dispose方法中輸出相應的文字以確定對象回收的時機。

1: public?interface IFoo {} 2: public?interface IBar {} 3: public?interface IBaz {} 4:? 5: public?class Foo : Disposable, IFoo {} 6: public?class Bar : Disposable, IBar {} 7: public?class Baz : Disposable, IBaz {} 8:? 9: public?class Disposable : IDisposable 10: { 11:???? public?void Dispose() 12:???? { 13:???????? Console.WriteLine("{0}.Dispose()", this.GetType()); 14:???? } 15: }

我們在作為程序入口的Main方法中創建了一個ServiceCollection對象,并在其中采用不同的生命周期管理模式注冊了三個相應的服務(IFoo/Foo、IBar/Bar和IBaz/Baz分別采用Transient、Scoped和Singleton模式)。我們針對這個ServiceCollection創建了一個ServiceProvider(root),以及它的兩個“兒子”(child1和child2)。在分別通過child1和child2提供了兩個服務實例(child1:IFoo, child2:IBar/IBaz)之后,我們先后調用三個ServiceProvider(child1=>child2=>root)的Dispose方法。

1: class Program 2: { 3:???? static?void Main(string[] args) 4:???? { 5:???????? IServiceProvider root = new ServiceCollection() 6:???????????? .AddTransient<IFoo, Foo>() 7:???????????? .AddScoped<IBar, Bar>() 8:???????????? .AddSingleton<IBaz, Baz>() 9:???????????? .BuildServiceProvider(); 10:???????? IServiceProvider child1 = root.GetService<IServiceScopeFactory>().CreateScope().ServiceProvider; 11:???????? IServiceProvider child2 = root.GetService<IServiceScopeFactory>().CreateScope().ServiceProvider; 12:? 13:???????? child1.GetService<IFoo>(); 14:???????? child1.GetService<IFoo>(); 15:???????? child2.GetService<IBar>(); 16:???????? child2.GetService<IBaz>(); 17:? 18:???????? Console.WriteLine("child1.Dispose()"); 19:???????? ((IDisposable)child1).Dispose(); 20:? 21:???????? Console.WriteLine("child2.Dispose()"); 22:???????? ((IDisposable)child2).Dispose(); 23:? 24:???????? Console.WriteLine("root.Dispose()"); 25:???????? ((IDisposable)root).Dispose(); 26:???? } 27: }

該程序運行之后會在控制臺上產生如下的輸出結果。從這個結果我們不難看出由child1提供的兩個采用Transient模式的服務實例的回收實在child1的Dispose方法執行之后自動完成的。當child2的Dispose方法被調用的時候,對于由它提供的兩個服務對象來說,只有注冊時采用Scope模式的Bar對象被自動回收了,至于采用Singleton模式的Baz對象的回收工作,是在root的Dispose方法被調用之后自動完成的。

1: child1.Dispose() 2: Foo.Dispose() 3: Foo.Dispose() 4: child2.Dispose() 5: Bar.Dispose() 6: root.Dispose() 7: Baz.Dispose()

了解ServiceProvider針對不同生命周期管理模式所采用的服務回收策略還會幫助我們正確的使用它。具體來說,當我們在使用一個現有的ServiceProvider的時候,由于我們并不能直接對它實施回收(因為它同時會在其它地方被使用),如果直接使用它來提供我們所需的服務實例,由于這些服務實例可能會在很長一段時間得不到回收,進而導致一些內存泄漏的問題。如果所用的是一個與當前應用具有相同生命周期(ServiceProvider在應用終止的時候才會被回收)的ServiceProvider,而且提供的服務采用Transient模式,這個問題就更加嚴重了,這意味著每次提供的服務實例都是一個全新的對象,但是它永遠得不到回收。

為了解決這個問題,我想很多人會想到一種解決方案,那就是按照如下所示的方式顯式地對提供的每個服務實例實施回收工作。實際上這并不是一種推薦的編程方式,因為這樣的做法僅僅確保了服務實例對象的Dispose方法能夠被及時調用,但是ServiceProvider依然保持著對服務實例的引用,后者依然不能及時地被GC回收。

1: public?void DoWork(IServiceProvider serviceProvider) 2: { 3:???? using (IFoobar foobar = serviceProvider.GetService<IFoobar>()) 4:???? { 5:???????? ... 6:???? } 7: }

或者

1: public?void DoWork(IServiceProvider serviceProvider) 2: { 3:???? IFoobar foobar = serviceProvider.GetService<IFoobar>(); 4:???? try 5:???? { 6:???????? ... 7:???? } 8:???? finally 9:???? { 10:???????? (foobar as IDisposable)?.Dispose(); 11:???? } 12: }

由于提供的服務實例總是被某個ServiceProvider引用著[1](直接提供服務實例的ServiceProvider或者是它的根),所以服務實例能夠被GC從內存及時回收的前提是引用它的ServiceProvider及時地變成垃圾對象。要讓提供服務實例的ServiceProvider成為垃圾對象,我們就必須創建一個新的ServiceProvider,通過上面的介紹我們知道ServiceProvider的創建可以通過創建ServiceScope的方式來實現。除此之外,為我們可以通過回收ServiceScope的方式來回收對應的ServiceProvider,進而進一步回收由它提供的服務實例(僅限Transient和Scoped模式)。下面的代碼片段給出了正確的編程方式。

1: public?void DoWork(IServiceProvider serviceProvider) 2: { 3:???? using (IServiceScope serviceScope = serviceProvider.GetService<IServiceScopeFactory>().CreateScope()) 4:???? { 5:???????? IFoobar foobar = serviceScope.ServiceProvider.GetService<IFoobar>(); 6:???????? ... 7:???? } 8: }

接下來我們通過一個簡單的實例演示上述這兩種針對服務回收的編程方式之間的差異。我們在一個控制臺應用中定義了一個繼承自IDisposable的服務接口IFoobar和實現它的服務類Foobar。如下面的代碼片段所示,為了確認對象真正被GC回收的時機,我們為Foobar定義了一個析構函數。在該析構函數和Dispose方法中,我們還會在控制臺上輸出相應的指導性文字。

1: public?interface IFoobar: IDisposable 2: {} 3:? 4: public?class Foobar : IFoobar 5: { 6:???? ~Foobar() 7:???? { 8:???????? Console.WriteLine("Foobar.Finalize()"); 9:???? } 10:? 11:???? public?void Dispose() 12:???? { 13:???????? Console.WriteLine("Foobar.Dispose()"); 14:???? } 15: }

在作為程序入口的Main方法中,我們創建了一個ServiceCollection對象并采用Transient模式將IFoobbar/Foobar注冊其中。借助于通過該ServiceCollection創建的ServiceProvider,我們分別采用上述的兩種方式獲取服務實例并試圖對它實施回收。為了強制GC試試垃圾回收,我們顯式調用了GC的Collect方法。

1: class Program 2: { 3:???? static?void Main(string[] args) 4:???? { 5:???????? IServiceProvider serviceProvider = new ServiceCollection() 6:???????????? .AddTransient<IFoobar, Foobar>() 7:???????????? .BuildServiceProvider(); 8:? 9:???????? serviceProvider.GetService<IFoobar>().Dispose(); 10:???????? GC.Collect(); 11:? 12:???????? Console.WriteLine("----------------"); 13:???????? using (IServiceScope serviceScope = serviceProvider.GetService<IServiceScopeFactory>().CreateScope()) 14:???????? { 15:???????????? serviceScope.ServiceProvider.GetService<IFoobar>(); 16:???????? } 17:???????? GC.Collect(); 18:? 19:???????? Console.Read(); 20:???? } 21: }

該程序執行之后會在控制臺上產生如下所示的輸出結果。從這個結果我們可以看出,如果我們使用現有的ServiceProvider來提供所需的服務實例,后者在GC進行垃圾回收之前并不會從內存中釋放。如果我們利用現有的ServiceProvider創建一個ServiceScope,并利用它所在的ServiceProvider來提供我們所需的服務實例,GC是可以將其從內存中釋放出來的。

1: Foobar.Dispose() 2: ---------------- 3: Foobar.Dispose() 4: Foobar.Finalize()

[1] 對于分別采用 Scoped和Singleton模式提供的服務實例,當前ServiceProvider和根ServiceProvider分別具有對它們的引用。如果采用Transient模式,只有服務類型實現了IDisposable接口,當前ServiceProvider才需要對它保持引用以完成對它們的回收,否則沒有任何一個ServiceProvider保持對它們的引用。

ASP.NET Core中的依賴注入(1):控制反轉(IoC)
ASP.NET Core中的依賴注入(2):依賴注入(DI)
ASP.NET Core中的依賴注入(3):服務注冊與提取
ASP.NET Core中的依賴注入(4):構造函數的選擇與生命周期管理
ASP.NET Core中的依賴注入(5):ServicePrvider實現揭秘【總體設計】
ASP.NET Core中的依賴注入(5):ServicePrvider實現揭秘【解讀ServiceCallSite】
ASP.NET Core中的依賴注入(5):ServicePrvider實現揭秘【補充漏掉的細節】


作者:蔣金楠? 微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。 原文鏈接

總結

以上是生活随笔為你收集整理的ASP.NET Core中的依赖注入(4): 构造函数的选择与服务生命周期管理的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 一级看片免费视频 | 6080成人 | 成人免费视频久久 | 色干干 | 不卡中文字幕在线观看 | 免费在线观看网址入口 | 狠狠操狠狠摸 | 日本精品人妻无码免费大全 | 综合性色 | 鲍鱼av在线 | 亚洲精品在线播放视频 | 黄色喷水视频 | 伊人青青久久 | 永久免费看mv网站入口亚洲 | 日本少妇激情 | 国产性生活 | wwwxxx在线 | 99精品欧美一区二区三区 | 日本一区二区三区免费电影 | 亚洲国产黄色片 | 国产精品日 | 一区二区不卡在线观看 | 国产日韩欧美专区 | 4438x五月天 日吊视频 | 人妖ts福利视频一二三区 | 中文字幕日韩高清 | 国语毛片 | 国产一级二级在线观看 | 国产视频一区二区不卡 | 视频免费1区二区三区 | 欧美成人黑人猛交 | 日b免费视频 | 亚洲午夜精品福利 | 亚洲精品国产精品国自产网站 | 想要xx视频 | 日本精品一区二区三区在线观看 | 久久久影院 | 波多野结衣一区二区三区高清av | 国产波霸爆乳一区二区 | 999国内精品永久免费视频 | 91黄漫 | 欧美日韩国产一区在线 | 人妻丰满熟妇av无码区免 | 乖女从小调教h尿便器小说 欧美韩一区二区 | 在线观看www视频 | 五月天色视频 | 精品无码国产一区二区三区51安 | 久久久久九九 | 人人射影院 | 久久网免费视频 | 国产成人免费看一级大黄 | 麻豆av导航 | 国产日韩在线观看视频 | 国产在线精品播放 | 亚洲天堂精品在线 | 琪琪伦伦影院理论片 | 农村妇女av | 亚洲高清视频在线 | 黄色成人av | 五月天三级 | 国精品无码一区二区三区 | 国产成人观看 | 亚洲激情视频在线播放 | 成人精品一区二区三区中文字幕 | 探花av在线| 欧美国产日韩一区二区 | 国产精品无码一区二区桃花视频 | 操操操操操操 | 天堂av一区二区三区 | 清清草在线视频 | 成人性生交大片免费看96 | 黄色午夜网站 | 国产剧情一区二区三区 | 日剧网| 国产极品91 | 中文字幕第22页 | 日韩欧美在线精品 | 中国老妇性视频 | 美女娇喘| 黄色片网站在线观看 | 91久久综合精品国产丝袜蜜芽 | 色天天天| 日日干夜夜撸 | 爱的色放3| 久久狠狠高潮亚洲精品 | 日韩一区av在线 | 久久久久久久久久久久久国产 | 在线视频在线观看 | 亚洲第一成人网站 | 这里只有精品视频在线观看 | 大地资源在线观看免费高清版粤语 | 久久亚洲av成人无码国产电影 | 色眯眯影视 | 国产精品免费一区二区三区在线观看 | 亚洲专区av | 色欧美亚洲 | 操操网| 99精品一区二区三区无码吞精 | 久久免费少妇高潮99精品 |