• 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

  • 相关阅读:
    ant
    Java中的值传递和引用传递
    待解决的问题
    Ant生成文件解析
    JUnit初学
    遍历枚举
    2013年5月阅读链接
    《C Primer Plus》阅读笔记(3)
    《C Primer Plus》阅读笔记(2)
    《C Primer Plus》阅读笔记(4)
  • 原文地址:https://www.cnblogs.com/NCoreCoder/p/13555199.html
Copyright © 2020-2023  润新知