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

Asp.net Core启动流程讲解(四)的更多相关文章

  1. ASP.NET Core启动流程

    1. 引言 对于ASP.NET Core应用程序来说,我们要记住非常重要的一点是:其本质上是一个独立的控制台应用,它并不是必需在IIS内部托管且并不需要IIS来启动运行(而这正是ASP.NET Cor ...

  2. 一张图理清ASP.NET Core启动流程

    1. 引言 对于ASP.NET Core应用程序来说,我们要记住非常重要的一点是:其本质上是一个独立的控制台应用,它并不是必需在IIS内部托管且并不需要IIS来启动运行(而这正是ASP.NET Cor ...

  3. Asp.net Core 启动流程分析

    新建的.net core 程序启动本质上是一个控制台应用程序,所以它的入口在Main方法中,所以启动的开始时从Main方法开始. public class Program { public stati ...

  4. Asp.net core 启动流程

  5. 如何在ASP.NET Core中构造UrlHelper,及ASP.NET Core MVC路由讲解

    参考文章: Unable to utilize UrlHelper 除了上面参考文章中介绍的方法,其实在ASP.NET Core MVC的Filter拦截器中要使用UrlHelper非常简单.如下代码 ...

  6. C#中的函数式编程:递归与纯函数(二) 学习ASP.NET Core Razor 编程系列四——Asp.Net Core Razor列表模板页面

    C#中的函数式编程:递归与纯函数(二)   在序言中,我们提到函数式编程的两大特征:无副作用.函数是第一公民.现在,我们先来深入第一个特征:无副作用. 无副作用是通过引用透明(Referential ...

  7. 探索ASP.Net Core 3.0系列四:在ASP.NET Core 3.0的应用中启动时运行异步任务

    前言:在本文中,我将介绍ASP.NET Core 3.0 WebHost的微小更改如何使使用IHostedService在应用程序启动时更轻松地运行异步任务. 翻译 :Andrew Lock   ht ...

  8. asp.net core启动源码以及监听,到处理请求响应的过程

    摘要 asp.net core发布至今已经将近6年了,很多人对于这一块还是有些陌生,或者说没接触过:接触过的,对于asp.net core整个启动过程,监听过程,以及请求过程,响应过程也是一知半解,可 ...

  9. Pro ASP.Net Core MVC 6th 第四章

    第四章 C# 关键特征 在本章中,我描述了Web应用程序开发中使用的C#特征,这些特征尚未被广泛理解或经常引起混淆. 这不是关于C#的书,但是,我仅为每个特征提供一个简单的例子,以便您可以按照本书其余 ...

随机推荐

  1. 将HTML5封装成android应用APK

    借鉴了网上很多的教程终于是把整个过程走通了,最主要的还是墙的问题.... 先是要搭建环境(每一步都要检查,确保安装正确): 1.安装 nodejs 淘宝镜像: npm install -g cnpm ...

  2. Django学习路26_转换字符串大小写 upper,lower

    在 urls 中注册 url(r'getstr',views.getstr) 在 views.py 中添加函数 def getstr(request): string = 'abc' string_2 ...

  3. Python time gmtime()方法

    描述 Python time gmtime() 函数将一个时间戳转换为UTC时区(0时区)的struct_time,高佣联盟 www.cgewang.com 可选的参数sec表示从1970-1-1以来 ...

  4. Skill art函数遍历字典

    https://www.cnblogs.com/yeungchie/ code procedure(ycartGo(length1) prog(() for(x 1 length1 printf(&q ...

  5. EACCES: permission denied,mkdir … npm install 安装依赖问题解决

    强哥最近在用hugeGraph图库做二次开发的时候,在打包的时遇到前端项目打包失败的问题: cwebp-bin@4.0.0 postinstall /home/hugegraph/my-hugegra ...

  6. x86架构: 硬件启动过程分析(附引导启动代码)

    用户按下开机键,几秒的时间,都经历了啥? 1.cpu各个寄存器赋初始值,cs.base=0xffff0000, eip=0xfff0,其他寄存器都是0,这时cs:ip得到的物理地址:0xfffffff ...

  7. java进阶(3)--接口

    一.基本概念 1.接口为引用数据类型,编译后也是class字节码文件 2.接口是完全抽象的,(抽象类是半抽象的),属于特殊的抽象类 3.接口定义方法:[修饰符列表]interface 接口名{} 4. ...

  8. three.js 着色器材质之glsl内置函数

    郭先生发现在开始学习three.js着色器材质时,我们经常会无从下手,辛苦写下的着色器,也会因莫名的报错而手足无措.原因是着色器材质它涉及到另一种语言–GLSL,只有懂了这个语言,我们才能更好的写出着 ...

  9. Python精选库大全,建议收藏留用!

    Python为啥这么火,这么多人学,就是因为简单好学,功能强大,整个社区非常活跃,资料很多.而且这语言涉及了方方面面,比如自动化测试,运维,爬虫,数据分析,机器学习,金融领域,后端开发,云计算,游戏开 ...

  10. 【av68676164(p31-p32)】Windows和Linux同步机制

    4.6.1 Windows同步机制 临界区(CRITICAL_SECTION) 在进程内使用,保证仅一个线程可以申请到该对象 临界区内是临界资源的访问 相关的API函数 初始化临界区 WINBASEA ...