ASP.NET Core - 依赖注入

  考虑到主题问题,在这里不打算详细讲解依赖注入的概念,须要了解依赖注入的能够关注个人DI&IoC分类讲解,这里咱们专一于ASP.NET Core 体系中系统自带的原生IoC容器是如何让咱们实现注入和解析的。html

  服务的生命周期  

  在开始以前,咱们先了解一下服务的生命周期,这仅涉及到IServiceCollectionIServiceProvider两个核心对象,这也是咱们开篇文章中阐述的两个重要对象。app

  在.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对象里面。 ide

  在此须要解释一下服务的生命周期源码分析

  Singleton:整个应用程序生命周期内只建立一个实例 ui

  Transient:每一次请求都会建立一个新的实例this

  Scoped:  每次从同一个容器中获取的实例是相同的、  spa

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注册的服务类型提取相应的服务对象。  code

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>();   }  }

 

  总体的一个依赖注入流程以下图htm

    

 

  追本溯源

  在上面咱们知道了如何经过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();   
}

   在这里咱们再也不深究一个完整的流程的每一个阶段,后面源码分析的时候会结合整个流程展现。

相关文章
相关标签/搜索