文章出處

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: }

3-11雖然在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實現揭秘【補充漏掉的細節】


文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()