zoukankan      html  css  js  c++  java
  • 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();   
    }

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

    如果你觉得该文章还不错且有所收获,请右下角推荐一下,也可留下您的问题或建议,让我们共同进步。 原创博客请在转载时保留原文链接或者在文章开头加上本人博客地址!
  • 相关阅读:
    Document
    Document
    Document
    Document
    Document
    Document
    Document
    Document
    C#中的委托是什么
    C# Lambda表达式Contains方法 like
  • 原文地址:https://www.cnblogs.com/lex-wu/p/10739517.html
Copyright © 2011-2022 走看看