我是靠谱客的博主 深情热狗,这篇文章主要介绍.Net 6中WebApplicationBuilder介绍和用法,现在分享给大家,希望可以做个参考。

介绍

.Net 6为我们带来的一种全新的引导程序启动的方式。与之前的拆分成Program.cs和Startup不同,整个引导启动代码都在Program.cs中。

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);在上篇文章中,我简要描述了如何使用 WebApplication和WebApplicationBuilder配置 ASP.NET Core 应用程序。在这篇文章中,我们来深入看下代码.

正文

我们示例程序的第一步是执行WebApplicationBuilder builder = WebApplication.CreateBuilder(args);创建一个WebApplicationBuilder实例。

从命令行中分配Args参数,并将选项对象传递给WebApplicationBuilder构造函数的WebApplicationOptions

复制代码
1
2
3
4
5
6
7
/// <summary> /// Initializes a new instance of the <see cref="WebApplicationBuilder"/> class with preconfigured defaults. /// </summary> /// <param name="args">Command line arguments</param> /// <returns>The <see cref="WebApplicationBuilder"/>.</returns> public static WebApplicationBuilder CreateBuilder(string[] args) => new(new() { Args = args });

WebApplicationOptions和WebApplicationBuilder后面在讲

复制代码
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
internal WebApplicationBuilder(WebApplicationOptions options, Action<IHostBuilder>? configureDefaults = null) /// <summary> /// Options for configuing the behavior for <see cref="WebApplication.CreateBuilder(WebApplicationOptions)"/>. /// </summary> public class WebApplicationOptions { /// <summary> /// The command line arguments. /// </summary> public string[]? Args { get; init; } /// <summary> /// The environment name. /// </summary> public string? EnvironmentName { get; init; } /// <summary> /// The application name. /// </summary> public string? ApplicationName { get; init; } /// <summary> /// The content root path. /// </summary> public string? ContentRootPath { get; init; } /// <summary> /// The web root path. /// </summary> public string? WebRootPath { get; init; } }

WebApplicationBuilder由一堆只读属性和一个方法组成Build(),该方法创建了一个WebApplication. 我删除了部分讲解用不到的代码。

如果您熟悉 ASP.NET Core,那么其中许多属性都使用以前版本中的常见类型

  • IWebHostEnvironment: 用于检索环境
  • IServiceCollection: 用于向 DI 容器注册服务。
  • ConfigurationManager: 用于添加新配置和检索配置值。我在之前的文章有讲
  • ILoggingBuilder: 用于注册额外的日志提供程序

在WebHost和Host性质很有趣,因为它们暴露出新的类型,ConfigureWebHostBuilder和ConfigureHostBuilder。这些类型分别实现IWebHostBuilder和IHostBuilder。

公开IWebHostBuilder和IHostBuilder接口对于允许从.NET 6 之前的应用程序迁移到新的最小托管,我们如何将的lambda风格配置IHostBuilder与命令式风格的WebApplicationBuilder协调起来,这就是ConfigureHostBuilder和ConfigureWebHostBuilder与一些内部沿来IHostBuilder实现。

复制代码
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
public sealed class WebApplicationBuilder { private const string EndpointRouteBuilderKey = "__EndpointRouteBuilder"; private readonly HostBuilder _hostBuilder = new(); private readonly BootstrapHostBuilder _bootstrapHostBuilder; private readonly WebApplicationServiceCollection _services = new(); private readonly List<KeyValuePair<string, string>> _hostConfigurationValues; private WebApplication? _builtApplication; /// <summary> /// Provides information about the web hosting environment an application is running. /// </summary> public IWebHostEnvironment Environment { get; } /// <summary> /// A collection of services for the application to compose. This is useful for adding user provided or framework provided services. /// </summary> public IServiceCollection Services { get; } /// <summary> /// A collection of configuration providers for the application to compose. This is useful for adding new configuration sources and providers. /// </summary> public ConfigurationManager Configuration { get; } /// <summary> /// A collection of logging providers for the application to compose. This is useful for adding new logging providers. /// </summary> public ILoggingBuilder Logging { get; } /// <summary> /// An <see cref="IWebHostBuilder"/> for configuring server specific properties, but not building. /// To build after configuration, call <see cref="Build"/>. /// </summary> public ConfigureWebHostBuilder WebHost { get; } /// <summary> /// An <see cref="IHostBuilder"/> for configuring host specific properties, but not building. /// To build after configuration, call <see cref="Build"/>. /// </summary> public ConfigureHostBuilder Host { get; } /// <summary> /// Builds the <see cref="WebApplication"/>. /// </summary> /// <returns>A configured <see cref="WebApplication"/>.</returns> public WebApplication Build() { // Wire up the host configuration here. We don't try to preserve the configuration // source itself here since we don't support mutating the host values after creating the builder. _hostBuilder.ConfigureHostConfiguration(builder => { builder.AddInMemoryCollection(_hostConfigurationValues); }); var chainedConfigSource = new TrackingChainedConfigurationSource(Configuration); // Wire up the application configuration by copying the already built configuration providers over to final configuration builder. // We wrap the existing provider in a configuration source to avoid re-bulding the already added configuration sources. _hostBuilder.ConfigureAppConfiguration(builder => { builder.Add(chainedConfigSource); foreach (var (key, value) in ((IConfigurationBuilder)Configuration).Properties) { builder.Properties[key] = value; } }); // This needs to go here to avoid adding the IHostedService that boots the server twice (the GenericWebHostService). // Copy the services that were added via WebApplicationBuilder.Services into the final IServiceCollection _hostBuilder.ConfigureServices((context, services) => { // We've only added services configured by the GenericWebHostBuilder and WebHost.ConfigureWebDefaults // at this point. HostBuilder news up a new ServiceCollection in HostBuilder.Build() we haven't seen // until now, so we cannot clear these services even though some are redundant because // we called ConfigureWebHostDefaults on both the _deferredHostBuilder and _hostBuilder. foreach (var s in _services) { services.Add(s); } // Add the hosted services that were initially added last // this makes sure any hosted services that are added run after the initial set // of hosted services. This means hosted services run before the web host starts. foreach (var s in _services.HostedServices) { services.Add(s); } // Clear the hosted services list out _services.HostedServices.Clear(); // Add any services to the user visible service collection so that they are observable // just in case users capture the Services property. Orchard does this to get a "blueprint" // of the service collection // Drop the reference to the existing collection and set the inner collection // to the new one. This allows code that has references to the service collection to still function. _services.InnerCollection = services; var hostBuilderProviders = ((IConfigurationRoot)context.Configuration).Providers; if (!hostBuilderProviders.Contains(chainedConfigSource.BuiltProvider)) { // Something removed the _hostBuilder's TrackingChainedConfigurationSource pointing back to the ConfigurationManager. // This is likely a test using WebApplicationFactory. Replicate the effect by clearing the ConfingurationManager sources. ((IConfigurationBuilder)Configuration).Sources.Clear(); } // Make builder.Configuration match the final configuration. To do that, we add the additional // providers in the inner _hostBuilders's Configuration to the ConfigurationManager. foreach (var provider in hostBuilderProviders) { if (!ReferenceEquals(provider, chainedConfigSource.BuiltProvider)) { ((IConfigurationBuilder)Configuration).Add(new ConfigurationProviderSource(provider)); } } }); // Run the other callbacks on the final host builder Host.RunDeferredCallbacks(_hostBuilder); _builtApplication = new WebApplication(_hostBuilder.Build()); // Mark the service collection as read-only to prevent future modifications _services.IsReadOnly = true; // Resolve both the _hostBuilder's Configuration and builder.Configuration to mark both as resolved within the // service provider ensuring both will be properly disposed with the provider. _ = _builtApplication.Services.GetService<IEnumerable<IConfiguration>>(); return _builtApplication; } private void ConfigureApplication(WebHostBuilderContext context, IApplicationBuilder app) { Debug.Assert(_builtApplication is not null); // UseRouting called before WebApplication such as in a StartupFilter // lets remove the property and reset it at the end so we don't mess with the routes in the filter if (app.Properties.TryGetValue(EndpointRouteBuilderKey, out var priorRouteBuilder)) { app.Properties.Remove(EndpointRouteBuilderKey); } if (context.HostingEnvironment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } // Wrap the entire destination pipeline in UseRouting() and UseEndpoints(), essentially: // destination.UseRouting() // destination.Run(source) // destination.UseEndpoints() // Set the route builder so that UseRouting will use the WebApplication as the IEndpointRouteBuilder for route matching app.Properties.Add(WebApplication.GlobalEndpointRouteBuilderKey, _builtApplication); // Only call UseRouting() if there are endpoints configured and UseRouting() wasn't called on the global route builder already if (_builtApplication.DataSources.Count > 0) { // If this is set, someone called UseRouting() when a global route builder was already set if (!_builtApplication.Properties.TryGetValue(EndpointRouteBuilderKey, out var localRouteBuilder)) { app.UseRouting(); } else { // UseEndpoints will be looking for the RouteBuilder so make sure it's set app.Properties[EndpointRouteBuilderKey] = localRouteBuilder; } } // Wire the source pipeline to run in the destination pipeline app.Use(next => { _builtApplication.Run(next); return _builtApplication.BuildRequestDelegate(); }); if (_builtApplication.DataSources.Count > 0) { // We don't know if user code called UseEndpoints(), so we will call it just in case, UseEndpoints() will ignore duplicate DataSources app.UseEndpoints(_ => { }); } // Copy the properties to the destination app builder foreach (var item in _builtApplication.Properties) { app.Properties[item.Key] = item.Value; } // Remove the route builder to clean up the properties, we're done adding routes to the pipeline app.Properties.Remove(WebApplication.GlobalEndpointRouteBuilderKey); // reset route builder if it existed, this is needed for StartupFilters if (priorRouteBuilder is not null) { app.Properties[EndpointRouteBuilderKey] = priorRouteBuilder; } } private sealed class LoggingBuilder : ILoggingBuilder { public LoggingBuilder(IServiceCollection services) { Services = services; } public IServiceCollection Services { get; } } }

ConfigureHostBuilder

复制代码
1
2
3
4
5
6
7
public sealed class ConfigureHostBuilder : IHostBuilder, ISupportsConfigureWebHost IHostBuilder ISupportsConfigureWebHost.ConfigureWebHost(Action<IWebHostBuilder> configure, Action<WebHostBuilderOptions> configureOptions) { throw new NotSupportedException("ConfigureWebHost() is not supported by WebApplicationBuilder.Host. Use the WebApplication returned by WebApplicationBuilder.Build() instead."); }

ConfigureHostBuilder实现IHostBuilder和ISupportsConfigureWebHost,但 ISupportsConfigureWebHost 的实现是假的什么意思呢?

这意味着虽然以下代码可以编译,但是会在运行时抛出异常。

复制代码
1
2
3
4
5
WebApplicationBuilder builder = WebApplication.CreateBuilder(args); builder.Host.ConfigureWebHost(webBuilder => { webBuilder.UseStartup<Startup>(); });

ConfigureServices(),该方法Action<>使用IServiceCollection从WebApplicationBuilder. 所以以下两个调用在功能上是相同的:

后一种方法显然不值得在正常实践中使用,但仍然可以使用依赖于这种方法的现有代码,

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
public IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate) { // Run these immediately so that they are observable by the imperative code configureDelegate(_context, _configuration); return this; } public IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate) { // Run these immediately so that they are observable by the imperative code configureDelegate(_context, _services); return this; }
复制代码
1
2
3
4
builder.Services.AddSingleton<MyImplementation>(); builder.Host.ConfigureServices((ctx, services) => services.AddSingleton<MyImplementation>());

并非所有委托ConfigureHostBuilder都立即传递给运行中的方法。例如UseServiceProviderFactory()保存在列表中,稍后在调用WebApplicationBuilder.Build()

复制代码
1
2
3
4
5
6
7
8
9
10
public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory) where TContainerBuilder : notnull { if (factory is null) { throw new ArgumentNullException(nameof(factory)); } _operations.Add(b => b.UseServiceProviderFactory(factory)); return this; }

BootstrapHostBuilder

回到ConfigureHostBuilder我们看内部的BootstrapHostBuilder,它记录了IHostBuilder收到的所有调用。例如ConfigureHostConfiguration()和ConfigureServices(),
这与ConfigureHostBuilder立即执行提供的委托相比,BootstrapHostBuilder的保存将委托提供给稍后执行的列表。这类似于泛型的HostBuilder工作方式。但请注意,这BootstrapHostBuilder调用Build()会引发异常

复制代码
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
internal class BootstrapHostBuilder : IHostBuilder { private readonly IServiceCollection _services; private readonly List<Action<IConfigurationBuilder>> _configureHostActions = new(); private readonly List<Action<HostBuilderContext, IConfigurationBuilder>> _configureAppActions = new(); private readonly List<Action<HostBuilderContext, IServiceCollection>> _configureServicesActions = new(); public IHost Build() { // HostingHostBuilderExtensions.ConfigureDefaults should never call this. throw new InvalidOperationException(); } public IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate) { _configureHostActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate))); return this; } public IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate) { // HostingHostBuilderExtensions.ConfigureDefaults calls this via ConfigureLogging _configureServicesActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate))); return this; } // ..... }

WebApplicationBuilder构造函数

最后我们来看WebApplicationBuilder构造函数,注释都给大家翻译了

复制代码
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
internal WebApplicationBuilder(WebApplicationOptions options, Action<IHostBuilder>? configureDefaults = null) { Services = _services; var args = options.Args; //尽早运行方法配置通用和web主机默认值,以从appsettings.json填充配置 //要预填充的环境变量(以DOTNET和ASPNETCORE为前缀)和其他可能的默认源 //正确的默认值。 _bootstrapHostBuilder = new BootstrapHostBuilder(Services, _hostBuilder.Properties); //不要在这里指定参数,因为我们希望稍后应用它们,以便 //可以覆盖ConfigureWebHostDefaults指定的默认值 _bootstrapHostBuilder.ConfigureDefaults(args: null); // This is for testing purposes configureDefaults?.Invoke(_bootstrapHostBuilder); //我们上次在这里指定了命令行,因为我们跳过了对ConfigureDefaults的调用中的命令行。 //args可以包含主机和应用程序设置,因此我们要确保 //我们适当地订购这些配置提供程序,而不复制它们 if (args is { Length: > 0 }) { _bootstrapHostBuilder.ConfigureAppConfiguration(config => { config.AddCommandLine(args); }); } // .... }
复制代码
1
2
3
4
5
6
7
8
9
10
11
// 自ConfigureWebHostDefaults覆盖特定于主机的设置(应用程序名称)以来,上次将参数应用于主机配置。 _bootstrapHostBuilder.ConfigureHostConfiguration(config => { if (args is { Length: > 0 }) { config.AddCommandLine(args); } // Apply the options after the args options.ApplyHostConfiguration(config); });

到此你可能都非常疑惑这玩意到底在干嘛。只要能看明白下面代码就好了,调用BootstrapHostBuilder.RunDefaultCallbacks(),
它以正确的顺序运行我们迄今为止积累的所有存储的回调,以构建HostBuilderContext. 该HostBuilderContext则是用来最终设定的剩余性能WebApplicationBuilder。

完成特定于应用程序的配置后,在由我们手动调用Build()创建一个WebApplication实例。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Configuration = new(); // Collect the hosted services separately since we want those to run after the user's hosted services _services.TrackHostedServices = true; // This is the application configuration var (hostContext, hostConfiguration) = _bootstrapHostBuilder.RunDefaultCallbacks(Configuration, _hostBuilder); // Stop tracking here _services.TrackHostedServices = false; // Capture the host configuration values here. We capture the values so that // changes to the host configuration have no effect on the final application. The // host configuration is immutable at this point. _hostConfigurationValues = new(hostConfiguration.AsEnumerable()); // Grab the WebHostBuilderContext from the property bag to use in the ConfigureWebHostBuilder var webHostContext = (WebHostBuilderContext)hostContext.Properties[typeof(WebHostBuilderContext)]; // Grab the IWebHostEnvironment from the webHostContext. This also matches the instance in the IServiceCollection. Environment = webHostContext.HostingEnvironment; Logging = new LoggingBuilder(Services); Host = new ConfigureHostBuilder(hostContext, Configuration, Services); WebHost = new ConfigureWebHostBuilder(webHostContext, Configuration, Services);

WebApplicationBuilder.Build()

该Build()方法不是非常复杂,首先是将配置的配置源复制到_hostBuilder的ConfigurationBuilder实现中。调用此方法时,builder它最初为空,因此这将填充由默认构建器扩展方法添加的所有源,以及您随后配置的额外源。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// source itself here since we don't support mutating the host values after creating the builder. _hostBuilder.ConfigureHostConfiguration(builder => { builder.AddInMemoryCollection(_hostConfigurationValues); }); _hostBuilder.ConfigureAppConfiguration(builder => { builder.Add(chainedConfigSource); foreach (var (key, value) in ((IConfigurationBuilder)Configuration).Properties) { builder.Properties[key] = value; } });

接下来,是一样的事情IServiceCollection,将它们从_services实例复制到_hostBuilder的集合中。

复制代码
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
// This needs to go here to avoid adding the IHostedService that boots the server twice (the GenericWebHostService). // Copy the services that were added via WebApplicationBuilder.Services into the final IServiceCollection _hostBuilder.ConfigureServices((context, services) => { // We've only added services configured by the GenericWebHostBuilder and WebHost.ConfigureWebDefaults // at this point. HostBuilder news up a new ServiceCollection in HostBuilder.Build() we haven't seen // until now, so we cannot clear these services even though some are redundant because // we called ConfigureWebHostDefaults on both the _deferredHostBuilder and _hostBuilder. foreach (var s in _services) { services.Add(s); } // Add the hosted services that were initially added last // this makes sure any hosted services that are added run after the initial set // of hosted services. This means hosted services run before the web host starts. foreach (var s in _services.HostedServices) { services.Add(s); } // Clear the hosted services list out _services.HostedServices.Clear(); // Add any services to the user visible service collection so that they are observable // just in case users capture the Services property. Orchard does this to get a "blueprint" // of the service collection // Drop the reference to the existing collection and set the inner collection // to the new one. This allows code that has references to the service collection to still function. _services.InnerCollection = services; var hostBuilderProviders = ((IConfigurationRoot)context.Configuration).Providers; if (!hostBuilderProviders.Contains(chainedConfigSource.BuiltProvider)) { // Something removed the _hostBuilder's TrackingChainedConfigurationSource pointing back to the ConfigurationManager. // This is likely a test using WebApplicationFactory. Replicate the effect by clearing the ConfingurationManager sources. ((IConfigurationBuilder)Configuration).Sources.Clear(); } // Make builder.Configuration match the final configuration. To do that, we add the additional // providers in the inner _hostBuilders's Configuration to the ConfigurationManager. foreach (var provider in hostBuilderProviders) { if (!ReferenceEquals(provider, chainedConfigSource.BuiltProvider)) { ((IConfigurationBuilder)Configuration).Add(new ConfigurationProviderSource(provider)); } } });

接下来运行我们在ConfigureHostBuilder属性中收集的任何回调

复制代码
1
2
// Run the other callbacks on the final host builder Host.RunDeferredCallbacks(_hostBuilder);

最后我们调用_hostBuilder.Build()构建Host实例,并将其传递给 的新实例WebApplication。调用_hostBuilder.Build()是调用所有注册回调的地方。

复制代码
1
_builtApplication = new WebApplication(_hostBuilder.Build());

最后,为了保持一切一致ConfigurationManager实例被清除,并链接到存储在WebApplication. 此外IServiceCollectiononWebApplicationBuilder被标记为只读,因此在调用后尝试添加服务WebApplicationBuilder将抛出一个InvalidOperationException. 最后WebApplication返回。

复制代码
1
2
3
4
5
6
7
8
// Mark the service collection as read-only to prevent future modifications _services.IsReadOnly = true; // Resolve both the _hostBuilder's Configuration and builder.Configuration to mark both as resolved within the // service provider ensuring both will be properly disposed with the provider. _ = _builtApplication.Services.GetService<IEnumerable<IConfiguration>>(); return _builtApplication;

差不多就是这样.

到此这篇关于.Net 6中WebApplicationBuilder介绍和用法的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持靠谱客。

最后

以上就是深情热狗最近收集整理的关于.Net 6中WebApplicationBuilder介绍和用法的全部内容,更多相关内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(142)

评论列表共有 0 条评论

立即
投稿
返回
顶部