Asp.net Core启动流程讲解(四)

Asp.net Core内 DI(DependencyInjection)贯穿了项目的始终,要学习Asp.net Core就无法越过DI。
下面讲解一下DI在Asp.Net Core内的流程

asp.net core 3.0以下

Asp.Net core 3.0以下有两种自定义替换DI容器的方式
替换 IServiceProviderFactory 的默认实现,以及IStartup.Configure 函数修改返回值

1、IServiceProviderFactory

查看 WebHostBuilder.Build

        public IWebHost Build()
        {
            var hostingServices = BuildCommonServices(out var hostingStartupErrors);
            var applicationServices = hostingServices.Clone();
            var hostingServiceProvider = GetProviderFromFactory(hostingServices);

            AddApplicationServices(applicationServices, hostingServiceProvider);

            var host = new WebHost(
                applicationServices,
                hostingServiceProvider,
                _options,
                _config,
                hostingStartupErrors);
            try
            {
                host.Initialize();
                
                //省略不重要的代码段

                return host;
            }
            catch
            {
                host.Dispose();
                throw;
            }

            IServiceProvider GetProviderFromFactory(IServiceCollection collection)
            {
                var provider = collection.BuildServiceProvider();
                var factory = provider.GetService<IServiceProviderFactory<IServiceCollection>>();

                if (factory != null && !(factory is DefaultServiceProviderFactory))
                {
                    using (provider)
                    {
                        return factory.CreateServiceProvider(factory.CreateBuilder(collection));
                    }
                }

                return provider;
            }
        }

再跟进 BuildCommonServices 函数

        private IServiceCollection BuildCommonServices(out AggregateException hostingStartupErrors)
        {
            var services = new ServiceCollection();

            //省略不重要的代码段

            services.AddTransient<IServiceProviderFactory<IServiceCollection>, DefaultServiceProviderFactory>();

            _configureServices?.Invoke(_context, services);

            return services;
        }

思路很简,注册DI,然后返回 IServiceProviderHost 容器,找到 IServiceProviderFactory ,判断类型不是
DefaultServiceProviderFactory,则转换DI,最后返回新的 IServiceProvider

       public interface IServiceProviderFactory<TContainerBuilder>
       {

              TContainerBuilder CreateBuilder(IServiceCollection services);

              IServiceProvider CreateServiceProvider(TContainerBuilder 
containerBuilder);
       }

WebHostBuilder.ConfigureServices 内DI注册 默认了实现的IServiceProviderFactory
IServiceProviderFactory 的流程是 IServiceProviderFactory.CreateBuilder->IServiceProviderFactory.CreateServiceProvider
先把DI容器 IServiceCollection 转换成自定义容器,再通过自定义容器转换成 IServiceProvider,最后依赖于根节点的IServiceProvider 即可

注册 IServiceProviderFactory 还有一处逻辑
WebHostBuilder.UseDefaultServiceProvider 转到源码

        public static IWebHostBuilder UseDefaultServiceProvider(this IWebHostBuilder hostBuilder, Action<WebHostBuilderContext, ServiceProviderOptions> configure)
        {
            return hostBuilder.ConfigureServices((context, services) =>
            {
                var options = new ServiceProviderOptions();
                configure(context, options);
                //替换默认的IServiceProviderFactory<IServiceCollection>
                services.Replace(ServiceDescriptor.Singleton<IServiceProviderFactory<IServiceCollection>>(new DefaultServiceProviderFactory(options)));
            });
        }

异曲同工之妙,不再赘述。

2、IStartup.Configure

还是打开熟悉的 WebHostBuilder.BuildCommonServices

        private IServiceCollection BuildCommonServices(out AggregateException hostingStartupErrors)
        {
            var services = new ServiceCollection();

            //忽略无关代码

            if (!string.IsNullOrEmpty(_options.StartupAssembly))
            {
                try
                {
                    var startupType = StartupLoader.FindStartupType(_options.StartupAssembly, _hostingEnvironment.EnvironmentName);

                    if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo()))
                    {
                        services.AddSingleton(typeof(IStartup), startupType);
                    }
                    else
                    {
                        services.AddSingleton(typeof(IStartup), sp =>
                        {
                            var hostingEnvironment = sp.GetRequiredService<IHostEnvironment>();
                            var methods = StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName);
                            return new ConventionBasedStartup(methods);
                        });
                    }
                }
                catch (Exception ex)
                {
                    var capture = ExceptionDispatchInfo.Capture(ex);
                    services.AddSingleton<IStartup>(_ =>
                    {
                        capture.Throw();
                        return null;
                    });
                }
            }

            _configureServices?.Invoke(_context, services);

            return services;
        }

注册 IStartup 到 DI

再查看 WebHostBuilder.Build

        public IWebHost Build()
        {
            var hostingServices = BuildCommonServices(out var hostingStartupErrors);
            var applicationServices = hostingServices.Clone();
            var hostingServiceProvider = GetProviderFromFactory(hostingServices);

            var host = new WebHost(
                applicationServices,
                hostingServiceProvider,
                _options,
                _config,
                hostingStartupErrors);
            try
            {
                host.Initialize();  //关建代码行
                
                //省略不重要的代码段

                return host;
            }
            catch
            {
                host.Dispose();
                throw;
            }
        }

查看 WebHost.Initialize 代码

        public void Initialize()
        {
            try
            {
                EnsureApplicationServices();
            }
            catch (Exception ex)
            {
                // EnsureApplicationServices may have failed due to a missing or throwing Startup class.
                if (_applicationServices == null)
                {
                    _applicationServices = _applicationServiceCollection.BuildServiceProvider();
                }

                if (!_options.CaptureStartupErrors)
                {
                    throw;
                }

                _applicationServicesException = ExceptionDispatchInfo.Capture(ex);
            }
        }

查看 WebHost.EnsureApplicationServices 代码

        private void EnsureApplicationServices()
        {
            if (_applicationServices == null)
            {
                EnsureStartup();
                _applicationServices = _startup.ConfigureServices(_applicationServiceCollection);
            }
        }

        private void EnsureStartup()
        {
            if (_startup != null)
            {
                return;
            }

            _startup = _hostingServiceProvider.GetService<IStartup>();

            if (_startup == null)
            {
                throw new InvalidOperationException($"No application configured. Please specify startup via IWebHostBuilder.UseStartup, IWebHostBuilder.Configure, injecting {nameof(IStartup)} or specifying the startup assembly via {nameof(WebHostDefaults.StartupAssemblyKey)} in the web host configuration.");
            }
        }

获取 IStartup 接口的实例,然后调用 IStartup.ConfigureServices 返回 IServiceProvider 实例

Asp.Net Core 3.0以上

Asp.Net Core 3.0以下的设计代码过于混乱,在Asp.Net Core 3.0开始,替换DI的流程变得简洁了

默认程序入口

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }

查看 Host.CreateDefaultBuilder

        public static IHostBuilder CreateDefaultBuilder(string[] args)
        {
            var builder = new HostBuilder();

            //忽略无关代码

            return builder;
        }

转向 HostBuilder.Build

        public IHost Build()
        {
            //忽略无关代码
            CreateServiceProvider();

            return _appServices.GetRequiredService<IHost>();
        }

查看 HostBuilder.CreateServiceProvider

        private void CreateServiceProvider()
        {
            var services = new ServiceCollection();
            //忽略无关代码

            var containerBuilder = _serviceProviderFactory.CreateBuilder(services);

            foreach (var containerAction in _configureContainerActions)
            {
                containerAction.ConfigureContainer(_hostBuilderContext, containerBuilder);
            }

            _appServices = _serviceProviderFactory.CreateServiceProvider(containerBuilder);

            if (_appServices == null)
            {
                throw new InvalidOperationException($"The IServiceProviderFactory returned a null IServiceProvider.");
            }

            // resolve configuration explicitly once to mark it as resolved within the
            // service provider, ensuring it will be properly disposed with the provider
            _ = _appServices.GetService<IConfiguration>();
        }

这里有个 _serviceProviderFactory,转到定义

    public class HostBuilder : IHostBuilder
    {
        //忽略无关代码
        private IServiceFactoryAdapter _serviceProviderFactory = new ServiceFactoryAdapter<IServiceCollection>(new DefaultServiceProviderFactory());
    }

对应传参,有两个函数不同重载的UseServiceProviderFactory

        public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory)
        {
            _serviceProviderFactory = new ServiceFactoryAdapter<TContainerBuilder>(factory ?? throw new ArgumentNullException(nameof(factory)));
            return this;
        }
        
        public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory)
        {
            _serviceProviderFactory = new ServiceFactoryAdapter<TContainerBuilder>(() => _hostBuilderContext, factory ?? throw new ArgumentNullException(nameof(factory)));
            return this;
        }        

后记

柠檬大佬说Asp.Net Core至少有三处可以替换DI的地方~ 还有一处
看了一下源码,大概是 IApplicationBuilderFactory

    public interface IApplicationBuilderFactory
    {
        IApplicationBuilder CreateBuilder(IFeatureCollection serverFeatures);
    }

这里IApplicationBuilderFactory.CreateBuilder返回 IApplicationBuilder
IApplicationBuilder里的Ioc对象可以在这里设置

如果对于内容有交流和学习的,可以加 .Net应用程序框架交流群,群号386092459

原文地址:https://www.cnblogs.com/NCoreCoder/p/13555199.html