ASP.NET Core - 依賴注入


  考慮到主題問題,在這里不打算詳細講解依賴注入的概念,需要了解依賴注入的可以關注我的DI&IoC分類講解,這里我們專注於ASP.NET Core 體系中系統自帶的原生IoC容器是如何讓我們實現注入和解析的。

  服務的生命周期  

  在開始之前,我們先了解一下服務的生命周期,這僅涉及到IServiceCollectionIServiceProvider兩個核心對象,這也是我們開篇文章中闡述的兩個重要對象。

  在.NET Core中DI的核心分為兩個組件:IServiceCollection和 IServiceProvider。

  • IServiceCollection               負責注冊
  • IServiceProvider                  負責提供實例
  • ActivatorUtilities(暫不講解)      負責提供實例,允許在依賴關系注入容器中創建沒有服務注冊的對象。

  服務注冊:

      public void ConfigureServices(IServiceCollection services)
        {
             services.AddTransient<ITransientTest, TransientTest>(); services.AddSingleton<ISingletonTest, SingletonTest>(); services.AddScoped<IScopedTest, ScopedTest>(); ..... }

  通過IServiceCollection這個對象,系統將相應的服務以不同的生命周期模式(Transient、Scoped和Singleton)注冊到ServiceCollection對象里面。 

  在此需要解釋一下服務的生命周期

  Singleton:整個應用程序生命周期內只創建一個實例 

  Transient:每一次請求都會創建一個新的實例

  Scoped:  每次從同一個容器中獲取的實例是相同的、  

interface ITransientTest { }
interface ISingletonTest { }
interface IScopedTest { }

class TransientTest : ITransientTest { }
class SingletonTest : ISingletonTest { }
class ScopedTest : IScopedTest { }

class Program
{
    static void Main(string[] args)
    {
        IServiceCollection services = new ServiceCollection();
        services = services.AddTransient<ITransientTest, TransientTest>();
        services = services.AddScoped<IScopedTest, ScopedTest>();
        services = services.AddSingleton<ISingletonTest, SingletonTest>();

        IServiceProvider serviceProvider = services.BuildServiceProvider();
         
        Console.WriteLine(ReferenceEquals(serviceProvider.GetService<ITransientTest>(), serviceProvider.GetService<ITransientTest>()));
        Console.WriteLine(ReferenceEquals(serviceProvider.GetService<IScopedTest>(), serviceProvider.GetService<IScopedTest>()));
        Console.WriteLine(ReferenceEquals(serviceProvider.GetService<ISingletonTest>(), serviceProvider.GetService<ISingletonTest>()));

        IServiceProvider serviceProvider1 = serviceProvider.CreateScope().ServiceProvider;
        IServiceProvider serviceProvider2 = serviceProvider.CreateScope().ServiceProvider;

        Console.WriteLine(ReferenceEquals(serviceProvider1.GetService<IScopedTest>(), serviceProvider1.GetService<IScopedTest>()));
        Console.WriteLine(ReferenceEquals(serviceProvider1.GetService<IScopedTest>(), serviceProvider2.GetService<IScopedTest>()));
        Console.WriteLine(ReferenceEquals(serviceProvider1.GetService<ISingletonTest>(), serviceProvider2.GetService<ISingletonTest>()));

        /* False
         * True
         * True
         * True
         * False
         * True
         */
    }
}

  對象解析:

    當我們需要從容器中解析一個對象出來的時候,用到了IServiceProvider對象,它根據IServiceCollection注冊的服務類型提取相應的服務對象。  

public class Test
{
    private readonly IServiceProvider _serviceProvider;
public Test(IServicCollection serviceCollection) { _serviceProvider = serviceCollection.BuildServiceProvider(); }   public TestController()   {   //如果沒有,則返回null   var testService1 = _serviceProvider.GetService<ITestService>();   //如果沒有,則拋出InvalidOperationException異常   var testService2 = _serviceProvider.GetRequiredService<ITestService>();   //獲取對應的集合   var testService3 = _serviceProvider.GetServices<ITestService>();     var testService4 = _serviceProvider.GetRequiredServices<ITestService>();   }  }

 

  整體的一個依賴注入流程如下圖

    

 

  追本溯源

  在上面我們知道了如何通過IServiceCollection和IServiceProvider這兩個對象注冊和解析對象,那么問題來了,這兩個對象是什么時候和如何創建的呢?

  回到我們ASP.NET Core - 從Program和Startup開始的控制台程序,在WebHostBuilder調用Build方法創建WebHost的過程中,這時會創建一個ServiceCollection對象,並將系統需要的一系列預定義服務(如IHostingEnvironment、IConfiguration、IHttpContextFactory、IStartupFilter等)注冊在它之上。接下來會利用這個ServiceCollection對象創建出對應的ServieProvider(BuildServiceProvider()),而這兩個ServiceProvider和ServiceCollection對象會一並傳遞給最終創建的WebHost,WebHost會利用這個ServiceProvider對象解析出Startup對象,並調用它的Configure方法用來完成對整個管道的建立。

  非常值得一提的是,Startup中的ConfigureServices方法是允許具有一個IServiceProvider類型的返回值,如果這個方法返回一個具體的ServiceProrivder,那么WebHostBuilder將不會利用ServiceCollection來創建新的ServiceProvider,而是直接使用這個返回的ServiceProvider來傳遞到Startup的Configure方法以供使用。我們后面會基於這個特性使用其他的IoC框架進行擴展替換。

   這是WebHost調用Startup類的Configure方法創建管道的過程

public void Initialize()
{
    _startup = _hostingServiceProvider.GetService<IStartup>();
    _applicationServices = _startup.ConfigureServices(_applicationServiceCollection);
    EnsureServer();
    var builderFactory = _applicationServices.GetRequiredService<IApplicationBuilderFactory>();
    var builder = builderFactory.CreateBuilder(Server.Features);
    builder.ApplicationServices = _applicationServices;

    var startupFilters = _applicationServices.GetService<IEnumerable<IStartupFilter>>();
    Action<IApplicationBuilder> configure = _startup.Configure;
    foreach (var filter in startupFilters.Reverse())
    {
        configure = filter.Configure(configure);
    }
    configure(builder);

    this._application = builder.Build();   
}

   在這里我們不再深究一個完整的流程的每個階段,后面源碼分析的時候會結合整個流程展示。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM