本文分析Program.cs 中Main()函数中代码的运行顺序分析asp.net core程序的启动,重点不是剖析源码,而是理清程序开始时执行的顺序。到底用了哪些实例,哪些法方。

asp.net core 3.1 的程序入口在项目Program.cs文件里,如下。

ususing System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; namespace WebDemo
{
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>();
});
}
}

1. Program类

program类是定义在项目根目录Program.cs文件中,所有.net core程序的入口,包括asp.net core 程序。这很有意思,就有点类似控制端程序。相比之前的web项目没有任何程序入口只有web.config、global这类配置文件和全局文件这种没有程序入口web项目,我觉得这类更容易理解asp.net core是怎么工作的。

在asp.net core 3.1中 的Program类里,定义了2个方法:Main() 和CreateHostBuilder()

2. 主程序入口Program.Main()方法

public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

Main() 方法是整个项目的入口方法,就如同C系列语言,所有的程序入口都是。这里main()只有一行代码,但是实际上执行了三个函数C:

  1 IHostBuilder builder= CreateHostBuilder(args);
2 IHost host=builder.Build();
3 host.Run();

第一行,是定义在Program类的CreateHostBuilder 它主要产生一个IhostBuilder实例builder。

第二行,通过builder.Build()法方产生一个Ihost实例 host。

第三含,通过host.Run()方法,开始运行web项目,这时候就可以响应各种请求了。

而这个过程里最繁琐的就是创建builder,本文重点篇幅就是在这里。

3. 创建并配置主机Builder:Program.CreateHostBuilder(args)法方

分解CreateHostBuilder(args) 的定义

此法方通过lamada表达式定义在Pogram类中。因为它就执行了一句化,所以可以用这种简便的方式定义(关于匿名函数、lamada、内置委托可以参考我之前的文章C# 匿名方法(函数) 匿名委托 内置泛型委托 lamada1)。为了方便阅读按照上面刨析Main()法方,所以它实际的定义如下:

  1         public static IHostBuilder CreateHostBuilder(string[] args)
2 {
3 IHostBuilder builder = Host.CreateDefaultBuilder(args);
4 Action < IWebHostBuilder > configAction = delegate(IWebHostBuilder webBuilder)
5 {
6 webBuilder.UseStartup<Startup>();
7 };
8 builder=builder.ConfigureWebHostDefaults(configAction);
9 return builder;
10 }

3.1. builder诞生 :生成IHostBuilder

将从Host.CreateHostBuilder()法方分析入手

CreateHostBuilder()法方的第一行代码是调用Host.CreateDefaultBuilder(args)法方,来创建IBuilder 对象实例。

IHostBuilder builder = Host.CreateDefaultBuilder(args);

IHostBuilder是一个非常重要的实例,有了这个实例才可以继续后续的加载更多的配置操作。通过IHostBuilder.Build()生产IHost实例。最终通过IHost.Run()运行项目。

3.1.1 Host类——用于产生初始的builder静态类

Host类是定义在Microsoft.Extensions.Hosting命名空间(Program.cs中引用了该命名公开)下的静态类.在Program.cs里引入。

声明如下:

  1 using Microsoft.Extensions.Hosting;
2
3 namespace Microsoft.Extensions.Hosting
4 {
5 public static class Host
6 {
7 public static IHostBuilder CreateDefaultBuilder();
8 public static IHostBuilder CreateDefaultBuilder(string[] args);
9 }
10 }

Host静态类就一个法方就是CreateDefaultBuilder() 。合计2个重载声明:一个带参数,一个不带参数。参考源码(.NET Core 3.0之深入源码理解Host(一)2)不带参数的重载实际在是将null作为参数调用带参数形式,如下:

  1 public static IHostBuilder CreateDefaultBuilder() =>CreateDefaultBuilder(args: null);
3.1.2 Host.CreateDefaultBuilder(args)方法
官方说明是用预先配置的默认值初始化一个Microsoft.Extensions.Hosting.HostBuilder实例(Initializes a new instance of the Microsoft.Extensions.Hosting.HostBuilder class with pre-configured defaults,详细见参考官方文档3)。
相关操作有:设置根目录ContentRootPath ;给Host.IConfiguration 加载:环境变量EnvironmentName,命令行参数args,配置文件IConfiguration.[EnvironmentName] json;还有加载日志模块等等。
详细见参考文档2、3,其中.NET Core 3.0之深入源码理解Host(一),作者从源码角度剖析了此法方,官方文档Host.CreateDefaultBuilder 方法官方说明了法方操作那些内容。

3.2. IHostBuilder转变成IWebHostBuilder

继续对Host.CreateHostBuilder()法方分析,在3.1建立了IHostBuilder实例后,将通过builder.ConfigureWebHostDefaults(Action <IWebHostBuilder >)方法将IHostBuilder实例转变为一个web主机性质的webbuilder(IWebHostBuilder)

IHoustBuilder.ConfigureWebHostDefaults(Action <IWebHostBuilder>)方法定义在Microsoft.Extensions.Hosting.GenericHostBuilderExtensions静态类中(GenericHostBuilderExtensions.cs),此静态类就定义这一个静态方法。

3.2.1. ConfigureWebHostDefaults()参数

此处参数configure是一个内置委托Action <IWebHostBuilder>,这个委托的定义就执行一行代码:
webBuilder.UseStartup<Startup>();

3.2.2. ConfigureWebHostDefaults()方法定义

其实ConfigureWebHostDefaults(Action <IWebHostBuilder >)方法是一个IHostBuilder的扩展方法(所以之前的写法并不准确,缺少了this builder参数,之前省略了)。
为了方便阅读法,用3.中的方式码剖析如下:
  1  	     public static IHostBuilder ConfigureWebHostDefaults(this IHostBuilder builder, Action<IWebHostBuilder> configure)
2 {
3 Action<IWebHostBuilder> webconfigure = delegate(IWebHostBuilder webHostBuilder)
4 {
5 WebHost.ConfigureWebDefaults(webHostBuilder);
6 configure(webHostBuilder);
7 };
8 return builder.ConfigureWebHost(webconfigure);
9 }
其实就调取了IHostBuilder的另一个扩展类ConfigureWebHost(Action<IWebHostBuilder>)(此扩展法方定义在Microsoft.Extensions.Hosting.GenericHostWebHostBuilderExtensions静态类中)。

3.2.2.1 把IHostBuilder转变为IWebHostBuilder: builder.ConfigureWebHost(webconfigure)法方

3.2.2.1.1 Action<IWebHostBuilder> webconfigure参数
内置委托,在这里对委托进行了定义,主要两行代码:
配置IWebHostBuilder的默认web设置。
WebHost.ConfigureWebDefaults(webHostBuilder);
在core2.1中该方法是在CreateHostBuilder()中WebHost.CreateDefaultBuilder(arg)中调用,core3.1,改为Host.ConfigureDefualts(arg)后,通过委托在ConfigureWebHost中调用。因为此方法在委托里调用,在下面章节降到委托执行的时候再详细说明。
调用参数传入的委托
configure(webHostBuilder);
 就是3.2.1中的
webBuilder.UseStartup<Startup>();
具体在下面章节讲解委托执行时在详细说明。
3.2.2.1.2 builder.ConfigureWebHost(webconfigure)法方定义
定义在Microsoft.Extensions.Hosting.GenericHostWebHostBuilderExtensions(GenericHostWebHostBuilderExtensions.cs) 
  1 using System;
2 using Microsoft.AspNetCore.Hosting;
3 using Microsoft.AspNetCore.Hosting.Internal;
4
5 namespace Microsoft.Extensions.Hosting
6 {
7 public static class GenericHostWebHostBuilderExtensions
8 {
9 public static IHostBuilder ConfigureWebHost(this IHostBuilder builder, Action<IWebHostBuilder> configure)
10 {
11 var webhostBuilder = new GenericWebHostBuilder(builder);
12 configure(webhostBuilder);
13 return builder;
14 }
15 }
16 }
Microsoft.AspNetCore.Hosting.Internal.GenericWebHostBuilder
在asp.net core 2.1 里IWebHostBuilder 在asp.net core 3.1里是怎么把泛型IHostBuilder生产webhost的builder的了,终于扒到它了。  
通过Microsoft.AspNetCore.Hosting.Internal.GenericWebHostBuilder类实例。GenericWebHostBuilder是一个内部类。这个类网上资源极少,甚至在官方文档里找不到说明,但可以参考其源码GenericWebHostBuilder.cs源代码。此类声明和其构造函数声明如下:
internal class GenericWebHostBuilder : IWebHostBuilder, ISupportsStartup, ISupportsUseDefaultServiceProvider
{
public GenericWebHostBuilder(IHostBuilder builder);
}
此类非常重要,它是Asp.net core 3.1 项目中IHostBuilder转变成IWebHostBuilder的基石或者源头 一个IHostBuilder实例最早就是通过此类构造函数实例化后变成了2.1里的IWebHostBuilder。实际干的就是注入了一些web有关的服务,详细建议查看源码(参考文档4
3.2.2.2 给IWebHostBuilder配置Web相关参数委托参数:终于执行委托了
configure(webhostBuilder);
在传入了那么多层委托后,终于我们扒到底了,在实现了IHostBuilder转变为IWebHostBuilder后,所有config有关的委托终于得以执行
  1 //在 builder.ConfigureWebHostDefaults()(-->在program.GreateHostBuilder()中)
2 //和下一句代码一同作为委托参数传入builder.ConfigureWebHost()
3 WebHost.ConfigureWebDefaults(webHostBuilder);
4 //Program.CreateHostBuilder()里的作为委托参数传入builder.ConfigureWebHostDefaults()
5 //再经过builder.ConfigureWebHostDefaults()合上一句一同作为委托传入builder.ConfigureWebHost()
6 webBuilder.UseStartup<Startup>();
3.2.2.2.1 WebHost.ConfigureWebDefualts(IWebHostBuilder)方法:
此方法在Asp.net core 2.1中 是WebHost.CreateDefaultBuilder(args) 里最后调用的方法,而在Asp.net core 3.1中,把IWebHostBuilder该为泛型IHostBuilder后,在执行IHostBuilder.ConfigureWebHost()时回调委托执行。此方法作用类似与Host.CreateDefaultBuilder(args),使用预配置配置一些关于Web方式的参数。主要是注入一些web相关的服务,配置主机地址等。此方法没有官方文档说明,建议直接查看源码WebHost.cs源代码(参考文档5)。
3.2.2.2.2 webBuilder.UseStartup<Startup>()法方:
UseStartup()是webbuilder的扩展发放,定义在Microsoft.AspNetCore.Hosting.WebHostBuilderExtensions静态类中(WebHostBuilderExtensions.cs),此方法申明如下
public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type startupType)

从方法名就可以看出是使用Startup类,这个方法主要使用反射技术,反射Startup类,响应startup类中的配置的信息

3.3. 最后梳理一下builder核心的代码

1.IHostBuilder builder = Host.CreateDefaultBuilder(args);
创建一个基础builder(读取app.json)
2.IWebHostBuilder webHostBuilder= new GenericWebHostBuilder(builder);
转换为webbuilder
3.WebHost.ConfigureWebDefaults(webHostBuilder);
给webbuilder使用预先的默认值配置
4.webBuilder.UseStartup<Startup>();
读取startup类配置信息

4.总结

aps.net core 3.1的程序启动代码分析下来。基本3个概念创建builder ,通过builder生成host ,最后使用host执行起来。其中builder及builder配置 ,host主机都是重要的概念。core 3.1 和 core2.1的区别,就是把IWebHostBuilder上再抽象一个IHostBuilder,可以是core 3.1的代码更加的灵活,以后的服务可以不单单是web服务。
另外asp.net core 的builder 配置中可以看到很多非常依赖,控制反转技术也就是注入依赖,好处就是想要什么服务就注册什么服务,更加灵活。
如果想深入学习,建议参考如下文章:

1. .NET Core 3.0之深入源码理解Host(一)
2. .NET Core 3.0之深入源码理解Host(二)
  作者的这个2个文章从源码角度简介了Ihostbuilder的相关知识剖析深度更深
3. 官方文档:.NET 通用主机 讲解IHostBuilder相关知识
4. 官方文档:ASP.NET Core 中的应用启动 介绍startup类相关知识


参考文档:
1. C# 匿名方法(函数) 匿名委托 内置泛型委托 lamada 作者:edzjx
2. .NET Core 3.0之深入源码理解Host(一) 作者:艾心
3. 官方文档Host.CreateDefaultBuilder 方法 作者:Micrsoft官方文档
4. GenericWebHostBuilder.cs源代码 作者:Asp.net@github 
5. WebHost.cs源代码 作者:Asp.net@github 
6. .NET Core 3.0之深入源码理解Host(二) 作者:艾心
7. 官方文档:.NET 通用主机 作者:Micrsoft官方文档
8. 官方文档:ASP.NET Core 中的应用启动 作者:Micrsoft官方文档

asp.net core 3.1 入口:Program.cs中的Main函数的更多相关文章

  1. 如何在 asp.net core 3.x 的 startup.cs 文件中获取注入的服务

    一.前言 从 18 年开始接触 .NET Core 开始,在私底下.工作中也开始慢慢从传统的 mvc 前后端一把梭,开始转向 web api + vue,之前自己有个半成品的 asp.net core ...

  2. 【Azure 应用服务】App Service .NET Core项目在Program.cs中自定义添加的logger.LogInformation,部署到App Service上后日志不显示Log Stream中的问题

    问题描述 在.Net Core 5.0 项目中,添加 Microsoft.Extensions.Logging.AzureAppServices 和 Microsoft.Extensions.Logg ...

  3. ASP.NET Core框架揭秘(持续更新中…)

    之前写了一系列关于.NET Core/ASP.NET Core的文章,但是大都是针对RC版本.到了正式的RTM,很多地方都发生了改变,所以我会将之前发布的文章针对正式版本的.NET Core 1.0进 ...

  4. ASP.NET Core框架揭秘(持续更新中…)

    之前写了一系列关于.NET Core/ASP.NET Core的文章,但是大都是针对RC版本.到了正式的RTM,很多地方都发生了改变,所以我会将之前发布的文章针对正式版本的.NET Core 1.0进 ...

  5. 【Asp.Net Core】在Visual Studio 2017中使用Asp.Net Core构建Angular4应用程序

    前言 Visual Studio 2017已经发布了很久了.做为集成了Asp.Net Core 1.1的地表最强IDE工具,越来越受.NET系的开发人员追捧. 随着Google Angular4的发布 ...

  6. ASP.NET Core 1.0: 指定Static File中的文件作为default page

    指定一个网站的default page是很容易的事情.譬如IIS Management中,可以通过default page来指定,而默认的index.html, index.htm之类,则早已经被设置 ...

  7. 【ASP.NET Core】如何隐藏响应头中的 “Kestrel”

    全宇宙人民都知道,ASP.NET Core 应用是不依赖服务器组件的,因此它可以独立运行,一般是使用支持跨平台的 Kestrel 服务器(当然,在 Windows 上还可以考虑用 HttpSys,但要 ...

  8. Asp.Net Core&Docker部署到树莓派3B中

    花了一点时间将吃灰数月的树莓派装上了Docker,并在容器中部署了一个Asp.Net Core应用程序,通过花生壳映射树莓派中的程序,可以使用外网访问树莓派,玩起来很有意思(外网访问地址:http:/ ...

  9. Asp.Net Core WebAPI+PostgreSQL部署在Docker中

     PostgreSQL是一个功能强大的开源数据库系统.它支持了大多数的SQL:2008标准的数据类型,包括整型.数值值.布尔型.字节型.字符型.日期型.时间间隔型和时间型,它也支持存储二进制的大对像, ...

随机推荐

  1. 【java基础】妙记进制转换

    一.二进制与十进制 1.正整数十进制转二进制 口诀:除二取余,倒序排列 解释:将一个十进制数除以二,得到的商再除以二,依此类推直到商等于一或零时为止,倒取将除得的余数,即换算为二进制数的结果 例如把5 ...

  2. spring cloud 与 docker 读书笔记 1

    Eureka Server 的高可用

  3. Centos搭建PXE,安装部署操作系统

    centos系统搭建PXE网络安装centos+ubuntu+Windows Centos搭建PXE,安装部署操作系统 一 . 原理: 1.什么是PXE: PXE(Pre-boot Execution ...

  4. 《ASP.NET Core 高性能系列》ASP.NET Core的启动过程(1)

    一.一切从头开始 简述:知道事情的真相就应该从头 开始,下面我们代码先行 public class Program { public static void Main(string[] args) { ...

  5. Centos 7 最小化部署svn版本控制(svn协议)

    1.关闭selinux sh-4.2# sed -i 's/enforcing/disabled/' /etc/selinux/config sh-4.2# reboot 2.卸载防火墙 sh-4.2 ...

  6. WSL初始配置+图形界面

    安装WSL 换源 参考另一篇文章https://www.cnblogs.com/bosslv/p/11006680.html 修改$PATH,默认会把windows的PATH也加入WSL中,不需要的话 ...

  7. java.io 包下的类有哪些 + 面试题

    java.io 包下的类有哪些 + 面试题 IO 介绍 IO 是 Input/Output 的缩写,它是基于流模型实现的,比如操作文件时使用输入流和输出流来写入和读取文件等. IO 分类 传统的 IO ...

  8. MD5加密解密以及设置salt(盐值)

    MD5算法 package com.oracle.jsp.util; import java.security.MessageDigest; import java.security.NoSuchAl ...

  9. 数学建模之优劣解距法(TOPSIS)

    优劣解距法简称TOPSIS,是一种综合评价方法,利用原始数据反映各评价方案之间的差距 优劣解距法的步骤通常为: 先将原始数据针具做正向化处理,得到正向化矩阵 再对正向化矩阵标准化处理以消除各指标纲量的 ...

  10. Idea使用插件实现逆向工程搭建SpringBoot项目

    之前写SpringBoot项目,每次都要手动去写实体类.dao层啥的,尤其是数据库表字段特别多的时候,特别麻烦.然后很多小伙伴都会用逆向工程来自动生成这些类,省去许多没必要的代码量,但是Mybatis ...