.NET5中 Host.CreateDefaultBuilder(args)详解

首先当我们创建.NET5 Web API 程序的时候,会生成下面这个 Program.cs 文件:

 1public class Program
 2{
 3    public static void Main(string[] args)
 4    {
 5        CreateHostBuilder(args).Build().Run();
 6    }
 7
 8    public static IHostBuilder CreateHostBuilder(string[] args) =>
 9        Host.CreateDefaultBuilder(args)
10            .ConfigureWebHostDefaults(webBuilder =>
11            {
12                webBuilder.UseStartup<Startup>();
13            });
14}

这个类包含两个方法:Main 和 CreateHostBuilder.

通过Host类调用CreateHostBuilder方法返回一个IHostBuilder对象,那么这个主机对象是什么呢?

Host类实现了IHost接口,用途如下:

  • 应用程序配置设置

  • 给应用程序添加依赖注入,配置.NET Core本机容器

  • 配置日志

Host.cs

Host 是一个静态类,包含了两个方法: CreateDefaultBuilder() 、CreateDefaultBuilder (args)

在下面源码中我将会剖析这两个方法的用途:

 1public static class Host
 2    {
 3       public static IHostBuilder CreateDefaultBuilder() =>
 4            CreateDefaultBuilder(args: null);
 5
 6        public static IHostBuilder CreateDefaultBuilder(string[] args)
 7        {
 8            var builder = new HostBuilder();
 9
10            builder.UseContentRoot(Directory.GetCurrentDirectory());
11            builder.ConfigureHostConfiguration(config =>
12            {
13                config.AddEnvironmentVariables(prefix: "DOTNET_");
14                if (args != null)
15                {
16                    config.AddCommandLine(args);
17                }
18            });
19
20            builder.ConfigureAppConfiguration((hostingContext, config) =>
21            {
22                IHostEnvironment env = hostingContext.HostingEnvironment;
23
24                bool reloadOnChange = hostingContext.Configuration.GetValue("hostBuilder:reloadConfigOnChange", defaultValue: true);
25
26                config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: reloadOnChange)
27                      .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: reloadOnChange);
28
29                if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName))
30                {
31                    var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
32                    if (appAssembly != null)
33                    {
34                        config.AddUserSecrets(appAssembly, optional: true);
35                    }
36                }
37
38                config.AddEnvironmentVariables();
39
40                if (args != null)
41                {
42                    config.AddCommandLine(args);
43                }
44            })
45            .ConfigureLogging((hostingContext, logging) =>
46            {
47                bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
48
49                if (isWindows)
50                {
51
52                    logging.AddFilter<EventLogLoggerProvider>(level => level >= LogLevel.Warning);
53                }
54
55                logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
56                logging.AddConsole();
57                logging.AddDebug();
58                logging.AddEventSourceLogger();
59
60                if (isWindows)
61                {
62                    logging.AddEventLog();
63                }
64
65                logging.Configure(options =>
66                {
67                    options.ActivityTrackingOptions = ActivityTrackingOptions.SpanId
68                                                        | ActivityTrackingOptions.TraceId
69                                                        | ActivityTrackingOptions.ParentId;
70                });
71
72            })
73            .UseDefaultServiceProvider((context, options) =>
74            {
75                bool isDevelopment = context.HostingEnvironment.IsDevelopment();
76                options.ValidateScopes = isDevelopment;
77                options.ValidateOnBuild = isDevelopment;
78            });
79
80            return builder;
81        }
82    }

首先无参的CreateDefaultBuilder()方法只是通过调用传递null值来调用有参的CreateDefaultBuilder(string[] args)方法。

var builder = new HostBuilder(); 

CreateDefaultBuilder(string[] args) 方法从创建HostBuilder 类型对象的实例化开始(继承自IHostBuilder),我们可以直接在这个实例上调用Build.Run()方法来启动我们的ASP.NET 主机,但是只是这样调用的话,并没有对我们的主机以及应用程序做配置,那么怎么配置呢?

Host Configuration(主机配置)

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

这个方法定义了Host 执行的目录,默认是根目录。

应用程序配置(Setting up the app)

 1builder.ConfigureAppConfiguration((hostingContext, config) =>
 2{
 3    IHostEnvironment env = hostingContext.HostingEnvironment;
 4
 5    bool reloadOnChange = hostingContext.Configuration.GetValue("hostBuilder:reloadConfigOnChange", defaultValue: true);
 6
 7    config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: reloadOnChange)
 8          .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: reloadOnChange);
 9
10    if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName))
11    {
12        var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
13        if (appAssembly != null)
14        {
15            config.AddUserSecrets(appAssembly, optional: true);
16        }
17    }
18
19    config.AddEnvironmentVariables();
20
21    if (args != null)
22    {
23        config.AddCommandLine(args);
24    }
25});

hostingContext.HostingEnvironment 表示我们在开发或者生产环境下的环境信息。

1bool reloadOnChange = hostingContext.Configuration.GetValue("hostBuilder:reloadConfigOnChange", defaultValue: true);
2
3config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: reloadOnChange)
4      .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: reloadOnChange); 

在程序启动配置时,它会加载我们项目中的appsettings.json及其下面的配置json 文件。通过AddJsonFile()这个方法来实现,这个方法有三个入参:

  • Path(string): .json 文件的相对路径位置

  • Optional(bool): 指定文件是否是必须的,如果设置为false,那么如果找不到文件则会抛出文件找不到异常。

  • ReloadOnchange(bool): 如果设置为true,那么我们改变配置文件,那么应用程序也会随之更改而无需重新启动。

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

这段代码是.NET5 新增的,在之前.net core的应用程序中,reloadOnchange的值始终是ture,至此我们就可以对hostBuilder:reloadConfigOnChange 配置为false;

1if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName)) 
2{ 
3    var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName)); 
4    if (appAssembly != null) 
5    { 
6        config.AddUserSecrets(appAssembly, optional: true); 
7    } 
8} 

这段程序的作用是如果我们需要加密我们的appsettings.json文件,则可以使用;用法如下:https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-3.1&tabs=windows

配置日志(Logging)

 1.ConfigureLogging((hostingContext, logging) =>
 2 {
 3     bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
 4
 5     // IMPORTANT: This needs to be added *before* configuration is loaded, this lets
 6     // the defaults be overridden by the configuration.
 7     if (isWindows)
 8     {
 9         // Default the EventLogLoggerProvider to warning or above
10         logging.AddFilter<EventLogLoggerProvider>(level => level >= LogLevel.Warning);
11     }
12
13     logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
14     logging.AddConsole();
15     logging.AddDebug();
16     logging.AddEventSourceLogger();
17
18     if (isWindows)
19     {
20         // Add the EventLogLoggerProvider on windows machines
21         logging.AddEventLog();
22     }
23
24     logging.Configure(options =>
25     {
26         options.ActivityTrackingOptions = ActivityTrackingOptions.SpanId
27                                             | ActivityTrackingOptions.TraceId
28                                             | ActivityTrackingOptions.ParentId;
29     });
30 })

上述代码用来设置应用程序的生成日志;从代码中可以看出如果程序跑在windows环境下,级别是或者高于警告级别的日志将会被输出在控制台,VS输出窗口以及写入系统日志服务。

依赖注入

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

CreateDefaultBuilder 方法的最后一步是将依赖注入添加到我们的应用程序中。UseDefaultServiceProvider这个方法用来添加.NET 5 或者.NET Core自带的IOC容器。