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,然后返回 IServiceProvider 给 Host 容器,找到 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启动流程讲解(四)的更多相关文章
- ASP.NET Core启动流程
1. 引言 对于ASP.NET Core应用程序来说,我们要记住非常重要的一点是:其本质上是一个独立的控制台应用,它并不是必需在IIS内部托管且并不需要IIS来启动运行(而这正是ASP.NET Cor ...
- 一张图理清ASP.NET Core启动流程
1. 引言 对于ASP.NET Core应用程序来说,我们要记住非常重要的一点是:其本质上是一个独立的控制台应用,它并不是必需在IIS内部托管且并不需要IIS来启动运行(而这正是ASP.NET Cor ...
- Asp.net Core 启动流程分析
新建的.net core 程序启动本质上是一个控制台应用程序,所以它的入口在Main方法中,所以启动的开始时从Main方法开始. public class Program { public stati ...
- Asp.net core 启动流程
- 如何在ASP.NET Core中构造UrlHelper,及ASP.NET Core MVC路由讲解
参考文章: Unable to utilize UrlHelper 除了上面参考文章中介绍的方法,其实在ASP.NET Core MVC的Filter拦截器中要使用UrlHelper非常简单.如下代码 ...
- C#中的函数式编程:递归与纯函数(二) 学习ASP.NET Core Razor 编程系列四——Asp.Net Core Razor列表模板页面
C#中的函数式编程:递归与纯函数(二) 在序言中,我们提到函数式编程的两大特征:无副作用.函数是第一公民.现在,我们先来深入第一个特征:无副作用. 无副作用是通过引用透明(Referential ...
- 探索ASP.Net Core 3.0系列四:在ASP.NET Core 3.0的应用中启动时运行异步任务
前言:在本文中,我将介绍ASP.NET Core 3.0 WebHost的微小更改如何使使用IHostedService在应用程序启动时更轻松地运行异步任务. 翻译 :Andrew Lock ht ...
- asp.net core启动源码以及监听,到处理请求响应的过程
摘要 asp.net core发布至今已经将近6年了,很多人对于这一块还是有些陌生,或者说没接触过:接触过的,对于asp.net core整个启动过程,监听过程,以及请求过程,响应过程也是一知半解,可 ...
- Pro ASP.Net Core MVC 6th 第四章
第四章 C# 关键特征 在本章中,我描述了Web应用程序开发中使用的C#特征,这些特征尚未被广泛理解或经常引起混淆. 这不是关于C#的书,但是,我仅为每个特征提供一个简单的例子,以便您可以按照本书其余 ...
随机推荐
- 使用 MySQLi 和 PDO 向 MySQL 插入多条数据
PHP MySQL 插入多条数据 使用 MySQLi 和 PDO 向 MySQL 插入多条数据 mysqli_multi_query() 函数可用来执行多条SQL语句. 以下实例向 "MyG ...
- C/C++编程笔记:C语言错误处理方法!如何更好地处理程序的错误?
C语言被忽视的一些小东西!C语言基础教程之错误处理. C 语言不提供对错误处理的直接支持,但是作为一种系统编程语言,它以返回值的形式允许您访问底层数据.在发生错误时,大多数的 C 或 UNIX 函数调 ...
- luogu P6583 回首过去 简单数论变换 简单容斥
LINK:回首过去 考试的时候没推出来 原因:状态真的很差 以及 数论方面的 我甚至连除数分块都给忘了. 手玩几个数据 可以发现 \(\frac{x}{y}\)满足题目中的条件当且仅当 这个是一个既约 ...
- js如何从一个数组中随机取出n个不同且不重复的值
前言 一位正在学习前端的菜鸟,虽菜,但还未放弃. 给大家画张图了解思路 以下是代码 function randomArr(arr,num){ let newArr = [];//创建一个新数组 for ...
- 31-关键字:final
final:最终的 1.可以用来修饰:类.方法.变量 2.具体的: 2.1 final 用来修饰一个类:此类不能被其他类所继承. * 比如:String类.System类.StringBuffer类 ...
- 牛逼了,利用Python实现“天眼系统”,只要照片就能了解个人信息
- vue-cookies 使用
import VueCookies from 'vue-cookies' Vue.use(VueCookies) $cookies.config() 设置默认值 $cookies.config(exp ...
- Java基础—控制流程语句(条件语句与循环结构)
与任何程序设计语言一样,Java使用条件语句和循环结构确定控制流程.Java的控制流程结构与C和C++的控制流程机构一样,只有很少的例外情况.没有goto语句,但break语句可以带标签,可以利用它实 ...
- 你知道MySQL是如何处理千万级数据的吗?
mysql 分表思路 一张一亿的订单表,可以分成五张表,这样每张表就只有两千万数据,分担了原来一张表的压力,分表需要根据某个条件进行分,这里可以根据地区来分表,需要一个中间件来控制到底是去哪张表去找到 ...
- canvas图片编辑操作:缩放、移动、保存(PC端+移动端)
最近在写canvas关于图片的操作,看了网上的代码基本都是不行的,于是就自己写了一个. html代码 <canvas id="myCanvas" width="37 ...