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

  • 相关阅读:
    【React Native】某个页面禁用物理返回键
    【React Native】DeviceEventEmitter监听通知及带参数传值
    转载【React Native代码】手写验证码倒计时组件
    【React Native】 中设置 APP 名称、应用图标、为安卓添加启动图
    【React Native错误集】* What went wrong: Execution failed for task ':app:installDebug'.
    【React Native错误集】Import fails with "Failed to execute 'ImportScripts' on 'WorkerGlobalScope'"
    【React Native错误集】Android error “Could not get BatchedBridge, make sure your bundle is packaged properly” on start of app
    「React Native笔记」在React的 setState 中操作数组和对象的多种方法(合集)
    【React Native】Error: Attribute application@allowBackup value=(false) from AndroidManifest.xml
    坚果云如何使用二次验证码/谷歌身份验证器/两步验证/虚拟MFA?
  • 原文地址:https://www.cnblogs.com/NCoreCoder/p/13555199.html
Copyright © 2011-2022 走看看