StartUp.cs文件
StartUp.cs中的疑惑点
- 为什么StartUp.cs中只有ConfigureServices,ConfigureContainer,Configure方法
- ConfigureServices方法的传入参数只能是IServiceCollection吗?
- ConfigureContainer方法的传入参数只能是对应三方Di的ContainerBuilder吗?
- Configure方法的传入参数只能IApplicationBuilder吗?
- StartUp.cs的构造函数参数只能是IConfiguration吗?
- StartUp.cs的构造函数参数只有一个参数吗?
了解项目中的StartUp.cs的作用
StartUp.cs作用始点
图(1)
图(2)
图(3)
图(4)
图(5)
看到上面那么多框框首先会非常的没抓点,不知道这些框框是在干什么,请先看一下以下对框的解释:
- 红色框代表的是主流程
- 蓝色框代表的是 执行UseStartUp()方法的委托方法代码块,在这里先称为
execute委托代码块
- 绿色框代表的是
execute委托代码块
又被包了一层委托,在执行execute委托代码块
之前先执行一些前置操作,这里层这个委托代码块为enhance委托代码块
- 黄色框代表的是 构建
execute委托代码块
和execute委托代码块
中传入的参数IWebHostBuilder
,先称为param委托代码块
解释完之后便可知道,**图(5)**中的内容,先是构造
param委托代码块
所需要的参数,然后调用param委托代码块
获取到IWebHostBuilder
,然后执行到enhance委托代码块
,再执行到execute委托代码块
,从而到UseStartUp<TStartUp>()
方法,并可知 webHostBuilder 对应的参数是GenericWebHostBuilder
加载StartUp.cs
-
源码(1)
public static IWebHostBuilder UseStartup<TStartup>( this IWebHostBuilder hostBuilder) where TStartup : class{ return hostBuilder.UseStartup(typeof (TStartup)); }
-
源码(2)
public static IWebHostBuilder UseStartup( this IWebHostBuilder hostBuilder, Type startupType){ if (hostBuilder is ISupportsStartup supportsStartup) return supportsStartup.UseStartup(startupType); string name = startupType.Assembly.GetName().Name; hostBuilder.UseSetting(WebHostDefaults.ApplicationKey, name); return hostBuilder.ConfigureServices((Action<IServiceCollection>) (services =>{ ///.... })); }
到这会先判断
GenericWebHostBuilder
是否有实现ISupportsStartup
接口,如果有实现则继续执行ISupportsStartup
的UseStartup(type)方法,没有的话,则往下走,实际上
GenericWebHostBuilder
实现了ISupportsStartup
接口 -
源码(3)
-
源码(4)
可以看到绿色框的委托方法会被注册到
this._builder
中,那他是注册到this._builder
哪呢?可以看到注册到了
HostBuilder
中的_configureServicesActions
(在ProGrame.cs中会执行.build()
方法就会调用注册到这里的委托方法了) -
源码(5)
此处可解答: (1)为什么Startup.cs的方法为什么只能叫Configure{EnvironmentName}Services,Configure{EnvironmentName}Container, Configure{EnvironmentName}
再继续看绿色框的委托方法干了什么
private void UseStartup( Type startupType, HostBuilderContext context, IServiceCollection services, object instance = null){ WebHostBuilderContext hostBuilderContext = this.GetWebHostBuilderContext(context); WebHostOptions property = (WebHostOptions) context.Properties[(object) typeof (WebHostOptions)]; ExceptionDispatchInfo startupError = (ExceptionDispatchInfo) null; ConfigureBuilder configureBuilder = (ConfigureBuilder) null; try { //如果StartUp.cs实现了IStartup接口,则抛异常 if (typeof (IStartup).IsAssignableFrom(startupType)){ //.... throw new NotSupportedException() } //如果StartUp.cs中无 Configure{0}Services 方法则抛出异常 if (StartupLoader.HasConfigureServicesIServiceProviderDelegate(startupType, context.HostingEnvironment.EnvironmentName)) { //.... throw new NotSupportedException() } //创建StartUp.cs实例,这里在 `源码(6)` 中会进行解析 if (instance == null) instance = ActivatorUtilities.CreateInstance((IServiceProvider) new GenericWebHostBuilder.HostServiceProvider(hostBuilderContext), startupType); //保存到HostBuilderContext中。 context.Properties[this._startupKey] = instance; //这里就是反射调用了StartUp.cs的Configure{0}Services方法,在 `源码(7)` 中会进行解析 IServiceProvider serviceProvider = StartupLoader.FindConfigureServicesDelegate( startupType, context.HostingEnvironment.EnvironmentName) .Build(instance)(services); //这里就是生成反射调用StartUp.cs的Configure{0}Container委托方法,但是未调用此方法 ConfigureContainerBuilder containerDelegate = StartupLoader.FindConfigureContainerDelegate( startupType, context.HostingEnvironment.EnvironmentName); if (containerDelegate.MethodInfo != (MethodInfo) null) { this._builder.Properties[(object) typeof (ConfigureContainerBuilder)] = (object) containerDelegate; //这里就是反射调用了StartUp.cs的Configure{0}Container方法,在`源码(8)` 中进行解析 InvokeContainer(this, containerDelegate); } //这里就是生成反射调用StartUp.cs的 Configure{0} 委托方法,但是未调用此方法 configureBuilder = StartupLoader.FindConfigureDelegate( startupType, context.HostingEnvironment.EnvironmentName); } catch (Exception ex) when (property.CaptureStartupErrors) { startupError = ExceptionDispatchInfo.Capture(ex); } //这里就是反射调用StartUp.cs的 Configure{0} 方法 services.Configure<GenericWebHostServiceOptions>((Action<GenericWebHostServiceOptions>) (options => options.ConfigureApplication = (Action<IApplicationBuilder>) (app => { startupError?.Throw(); if (instance == null || configureBuilder == null) return; //此处暂不进行解析 configureBuilder.Build(instance)(app); })));
-
源码(6)
此处可解答: (1)为什么Startup.cs的构造器无法引入其他的类型,只能是IConfiguration,IWebHostEnvironment这些
private void UseStartup( Type startupType, HostBuilderContext context, IServiceCollection services, object instance = null){ //..... instance = ActivatorUtilities.CreateInstance( (IServiceProvider) new GenericWebHostBuilder.HostServiceProvider(hostBuilderContext), startupType); //..... }
可以看到他new了一个serviceProvider,但是这个serviceProvider是被重写的,可以看一下GenericWebHostBuilder的实现
private sealed class HostServiceProvider : IServiceProvider{ private readonly WebHostBuilderContext _context; public HostServiceProvider(WebHostBuilderContext context) => this._context = context; public object GetService(Type serviceType){ if (serviceType == typeof (Microsoft.Extensions.Hosting.IHostingEnvironment) || serviceType == typeof (IHostingEnvironment) || serviceType == typeof (IWebHostEnvironment) || serviceType == typeof (IHostEnvironment)) return (object) this._context.HostingEnvironment; return serviceType == typeof (IConfiguration) ? (object) this._context.Configuration : (object) null; } }
可以看到他重写了GetService方法,当传入类型为
IHostingEnvironment or IHostingEnvironment or IWebHostEnvironment or IHostEnvironment
的时候,返回的均为this._context.HostingEnvironment
,当传入类型为IConfiguration
,返回的是this._context.Configuration
传入其他的就只会为null。这里就不具体看
ActivatorUtilities.CreateInstance
方法了,该方法实际上就是实例化传入的Type,而传入Type的构造函数参数均由serviceProvider提供,并且注意该Type只能提供一个构造器。从上面可得知:
StartUp.cs
只能有一个构造器StartUp.cs
构造器参数只能是IHostingEnvironment or IHostingEnvironment or IWebHostEnvironment or IHostEnvironment or IConfiguration
- 如果我们获取到全局的serviceProvider,使用用