前言

初始化比较复杂,文章拆分成3部分Host、WebHost、Startup,逐一分析

对象概念解释

接口定义

  • IHost : 主机抽象

    • IServiceProvider : 对象服务提供器
    • StartAsync : 启动方法
    • StopAsync : 停止方法
  • IHostBuilder : 主机构造器抽象

    • ConfigureHostConfiguration() : 配置主机配置文件
    • ConfigureAppConfiguration() : 配置应用配置文件
    • ConfigureServices() : 配置服务
    • UseServiceProviderFactory() : 配置服务提供商工厂
    • ConfigureContainer() : 配置容器
    • Build() : 构造主机

具体实现

  • Host : 主机

    • IHostLifetime : 主机生命周期
    • IServiceProvider : 服务提供商
    • ApplicationLifetime : 应用生命周期
    • HostOptions : 主机选项
    • IEnumerable<IHostedService> : 主机服务集合
  • HostBuilder : 主机构造器

    • _configureHostConfigActions : 主机配置委托集合
    • _configureAppConfigActions : 应用配置委托集合
    • _configureServicesActions : 服务委托集合
    • _configureContainerActions : 容器委托集合
    • _serviceProviderFactory : 服务提供商工厂

静态类

  • Host
    • CreateDefaultBuilder() 创建新的默认主机构建器

Host初始化创建

MVC项目创建时模板生成的代码,调用顺序CreateDefaultBuilder(HostBuilder) => ConfigureWebHostDefaults(HostBuilder) => WebHostBuilder

App应用创建CreateHostBuilder方法,创建Host构建器,再调用Build方法,生成Host对象,调用Run启动方法

  • Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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构建器

Microsoft.Extensions.Hosting.Host静态类里有个CreateDefaultBuilder方法创建HostBuilder,设置上述的一些配置的委托集合,返回Host构造器对象HostBuilder

CreateDefaultBuilder

无参方法

1
2
public static IHostBuilder CreateDefaultBuilder() =>
CreateDefaultBuilder(args: null);

可以看到该方法实际上是设置了默认值。

IHostBuilder CreateDefaultBuilder(string[] args)方法主要有以下功能:

创建HostBuilder对象

1
var builder = new HostBuilder();

设置内容根目录(UseContentRoot)

本质是调用ConfigureHostConfiguration方法,往主机配置文件委托集合_configureHostConfigActions里添加

1
builder.UseContentRoot(Directory.GetCurrentDirectory());

设置主机配置(ConfigureHostConfiguration)

把前缀是DOTNET_的环境变量加到配置里

1
2
3
4
5
6
7
8
builder.ConfigureHostConfiguration(config =>
{
config.AddEnvironmentVariables(prefix: "DOTNET_");
if (args != null)
{
config.AddCommandLine(args);
}
});

设置应用配置(ConfigureAppConfiguration)

配置初始化(环境变量、appsettings.json、User Secrets)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
builder.ConfigureAppConfiguration((hostingContext, config) =>
{
var env = hostingContext.HostingEnvironment;

var reloadOnChange = hostingContext.Configuration.GetValue("hostBuilder:reloadConfigOnChange", defaultValue: true);

config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: reloadOnChange)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: reloadOnChange);
if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName))
{
var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
if (appAssembly != null)
{
config.AddUserSecrets(appAssembly, optional: true);
}
}

config.AddEnvironmentVariables();

if (args != null)
{
config.AddCommandLine(args);
}
}

设置日志配置(ConfigureLogging)

加载配置文件的Logging部分,并根据平台环境,添加不同的日志实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
builder.ConfigureLogging((hostingContext, logging) =>
{
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);

// IMPORTANT: This needs to be added *before* configuration is loaded, this lets
// the defaults be overridden by the configuration.
if (isWindows)
{
// Default the EventLogLoggerProvider to warning or above
logging.AddFilter<EventLogLoggerProvider>(level => level >= LogLevel.Warning);
}

logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
logging.AddDebug();
logging.AddEventSourceLogger();

if (isWindows)
{
// Add the EventLogLoggerProvider on windows machines
logging.AddEventLog();
}
})

开发环境下开启作用域验证

1
2
3
4
5
6
builder.UseDefaultServiceProvider((context, options) =>
{
var isDevelopment = context.HostingEnvironment.IsDevelopment();
options.ValidateScopes = isDevelopment;
options.ValidateOnBuild = isDevelopment;
});

设置服务提供商配置(UseDefaultServiceProvider)

设置服务提供商选项,当前是开发环境

1
2
3
var isDevelopment = context.HostingEnvironment.IsDevelopment();
options.ValidateScopes = isDevelopment;
options.ValidateOnBuild = isDevelopment;

连接HostBuilder和WebHostBuilder

通过两个拓展方法连接HostBuilder和WebHostBuilder

  • ConfigureWebHostDefaults IHostBuilder拓展方法

文件位置:GenericHostBuilderExtensions.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static IHostBuilder ConfigureWebHostDefaults(this IHostBuilder builder, Action<IWebHostBuilder> configure)
{
if (configure is null)
{
throw new ArgumentNullException(nameof(configure));
}

return builder.ConfigureWebHost(webHostBuilder =>
{
WebHost.ConfigureWebDefaults(webHostBuilder);

configure(webHostBuilder);
});
}
  • ConfigureWebHost

文件位置:GenericHostWebHostBuilderExtensions.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public static IHostBuilder ConfigureWebHost(this IHostBuilder builder, Action<IWebHostBuilder> configure)
{
if (configure is null)
{
throw new ArgumentNullException(nameof(configure));
}

return builder.ConfigureWebHost(configure, _ => { });
}

public static IHostBuilder ConfigureWebHost(this IHostBuilder builder, Action<IWebHostBuilder> configure, Action<WebHostBuilderOptions> configureWebHostBuilder)
{
if (configure is null)
{
throw new ArgumentNullException(nameof(configure));
}

if (configureWebHostBuilder is null)
{
throw new ArgumentNullException(nameof(configureWebHostBuilder));
}

var webHostBuilderOptions = new WebHostBuilderOptions();
configureWebHostBuilder(webHostBuilderOptions);
var webhostBuilder = new GenericWebHostBuilder(builder, webHostBuilderOptions);
configure(webhostBuilder);
builder.ConfigureServices((context, services) => services.AddHostedService<GenericWebHostService>());
return builder;
}

与2.x版本的区别

在2.x里WebHost直接创建Server,构建Http管道,而3.x则是在Host主机内创建HostedService服务,并在HostedService内创建Server及Http管道

Host和WebHost都做了一些基础设置

  1. 设置 Content 根目录,将当前项目的根目录作为 ContentRoot 的目录。
  2. 读取 appsettinggs.json 配置文件,开发环境下的 UserSecrets 以及环境变量和命令行参数。
  3. 读取配置文件中的 Logging 节点,对日志系统进行配置。
  4. 设置开发环境下, ServiceProvider 的 ValidateScopes 为 true,避免直接在 Configure 方法中获取 Scope 实例。

从3.x开始保留了2.x中WebHost创建CreateDefaultBuilder方法,只是把涉及到Web这块业务抽出来单独成一个ConfigureWebDefaults(IWebHostBuilder builder)方法,方便HostBuilder和WebHostBuilder复用

Host启动流程

Build

此方法仅执行一次

BuildHostConfiguration

创建主机配置生成器ConfigurationBuilder,然后回调我们的代码提供的委托对配置对象的数据源进行设置,最终通过配置对象生成器Build生成配置对象

1
2
3
4
5
6
7
8
9
10
11
private void BuildHostConfiguration()
{
var configBuilder = new ConfigurationBuilder()
.AddInMemoryCollection(); // Make sure there's some default storage since there are no default providers

foreach (var buildAction in _configureHostConfigActions)
{
buildAction(configBuilder);
}
_hostConfiguration = configBuilder.Build();
}

CreateHostingEnvironment

创建HostingEnvironment(含义上面有说),默认用配置对象进行赋值,然后回调我们的代码提供的委托进一步设置主机环境对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void CreateHostingEnvironment()
{
_hostingEnvironment = new HostingEnvironment()
{
ApplicationName = _hostConfiguration[HostDefaults.ApplicationKey],
EnvironmentName = _hostConfiguration[HostDefaults.EnvironmentKey] ?? Environments.Production,
ContentRootPath = ResolveContentRootPath(_hostConfiguration[HostDefaults.ContentRootKey], AppContext.BaseDirectory),
};

if (string.IsNullOrEmpty(_hostingEnvironment.ApplicationName))
{
// Note GetEntryAssembly returns null for the net4x console test runner.
_hostingEnvironment.ApplicationName = Assembly.GetEntryAssembly()?.GetName().Name;
}

_hostingEnvironment.ContentRootFileProvider = new PhysicalFileProvider(_hostingEnvironment.ContentRootPath);
}

CreateHostBuilderContext

初始化构建器上下文(BuilderContext)

负责承载主机Host启动的一些环境变量HostingEnvironment、配置Configuration、属性Properties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
namespace Microsoft.Extensions.Hosting
{
/// <summary>
/// Context containing the common services on the <see cref="IHost" />. Some properties may be null until set by the <see cref="IHost" />.
/// </summary>
public class HostBuilderContext
{
public HostBuilderContext(IDictionary<object, object> properties)
{
Properties = properties ?? throw new System.ArgumentNullException(nameof(properties));
}

/// <summary>
/// The <see cref="IHostEnvironment" /> initialized by the <see cref="IHost" />.
/// </summary>
public IHostEnvironment HostingEnvironment { get; set; }

/// <summary>
/// The <see cref="IConfiguration" /> containing the merged configuration of the application and the <see cref="IHost" />.
/// </summary>
public IConfiguration Configuration { get; set; }

/// <summary>
/// A central location for sharing state between components during the host building process.
/// </summary>
public IDictionary<object, object> Properties { get; }
}
}

BuildAppConfiguration

创建配置构建起IConfigurationBuilder,负责承接配置的构建,最后把构建好的配置IConfiguration赋值给HostBuilderContext

1
2
3
4
5
6
7
8
9
10
11
12
13
private void BuildAppConfiguration()
{
IConfigurationBuilder configBuilder = new ConfigurationBuilder()
.SetBasePath(_hostingEnvironment.ContentRootPath)
.AddConfiguration(_hostConfiguration, shouldDisposeConfiguration: true);

foreach (Action<HostBuilderContext, IConfigurationBuilder> buildAction in _configureAppConfigActions)
{
buildAction(_hostBuilderContext, configBuilder);
}
_appConfiguration = configBuilder.Build();
_hostBuilderContext.Configuration = _appConfiguration;
}

CreateServiceProvider

创建对象服务容器ServiceCollection,把一些环境变量、生命周期、主机、配置等对象存放进去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
        private void CreateServiceProvider()
{
var services = new ServiceCollection();
#pragma warning disable CS0618 // Type or member is obsolete
services.AddSingleton<IHostingEnvironment>(_hostingEnvironment);
#pragma warning restore CS0618 // Type or member is obsolete
services.AddSingleton<IHostEnvironment>(_hostingEnvironment);
services.AddSingleton(_hostBuilderContext);
// register configuration as factory to make it dispose with the service provider
services.AddSingleton(_ => _appConfiguration);
#pragma warning disable CS0618 // Type or member is obsolete
services.AddSingleton<IApplicationLifetime>(s => (IApplicationLifetime)s.GetService<IHostApplicationLifetime>());
#pragma warning restore CS0618 // Type or member is obsolete
services.AddSingleton<IHostApplicationLifetime, ApplicationLifetime>();

AddLifetime(services);

services.AddSingleton<IHost>(_ =>
{
return new Internal.Host(_appServices,
_hostingEnvironment,
_defaultProvider,
_appServices.GetRequiredService<IHostApplicationLifetime>(),
_appServices.GetRequiredService<ILogger<Internal.Host>>(),
_appServices.GetRequiredService<IHostLifetime>(),
_appServices.GetRequiredService<IOptions<HostOptions>>());
});
services.AddOptions().Configure<HostOptions>(options => { options.Initialize(_hostConfiguration); });
services.AddLogging();

foreach (Action<HostBuilderContext, IServiceCollection> configureServicesAction in _configureServicesActions)
{
configureServicesAction(_hostBuilderContext, services);
}

object containerBuilder = _serviceProviderFactory.CreateBuilder(services);

foreach (IConfigureContainerAdapter containerAction in _configureContainerActions)
{
containerAction.ConfigureContainer(_hostBuilderContext, containerBuilder);
}

_appServices = _serviceProviderFactory.CreateServiceProvider(containerBuilder);

if (_appServices == null)
{
throw new InvalidOperationException(SR.NullIServiceProvider);
}

// 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>();
}