@@ -20,10 +20,13 @@ public sealed class WebApplicationBuilder
2020 private const string EndpointRouteBuilderKey = "__EndpointRouteBuilder" ;
2121
2222 private readonly HostBuilder _hostBuilder = new ( ) ;
23- private readonly ConfigureHostBuilder _deferredHostBuilder ;
24- private readonly ConfigureWebHostBuilder _deferredWebHostBuilder ;
23+ private readonly BootstrapHostBuilder _bootstrapHostBuilder ;
2524 private readonly WebHostEnvironment _environment ;
2625 private readonly WebApplicationServiceCollection _services = new ( ) ;
26+
27+ // This is effectively readonly because it is always set inline by the ConfigureWebHostDefaults callback in the ctor.
28+ private IWebHostBuilder _capturedWebHostBuilder = default ! ;
29+
2730 private WebApplication ? _builtApplication ;
2831
2932 internal WebApplicationBuilder ( Assembly ? callingAssembly , string [ ] ? args = null )
@@ -34,31 +37,34 @@ internal WebApplicationBuilder(Assembly? callingAssembly, string[]? args = null)
3437 Environment = _environment = new WebHostEnvironment ( callingAssembly ) ;
3538
3639 Configuration . SetBasePath ( _environment . ContentRootPath ) ;
37- Services . AddSingleton ( Environment ) ;
3840
3941 // Run methods to configure both generic and web host defaults early to populate config from appsettings.json
4042 // environment variables (both DOTNET_ and ASPNETCORE_ prefixed) and other possible default sources to prepopulate
4143 // the correct defaults.
42- var bootstrapBuilder = new BootstrapHostBuilder ( Configuration , _environment ) ;
43- bootstrapBuilder . ConfigureDefaults ( args ) ;
44- bootstrapBuilder . ConfigureWebHostDefaults ( configure : _ => { } ) ;
45- bootstrapBuilder . RunConfigurationCallbacks ( ) ;
44+ _bootstrapHostBuilder = new BootstrapHostBuilder ( Configuration , _environment , Services ) ;
45+ _bootstrapHostBuilder . ConfigureDefaults ( args ) ;
46+ _bootstrapHostBuilder . ConfigureWebHostDefaults ( webHostBuilder =>
47+ {
48+ // Runs inline.
49+ webHostBuilder . Configure ( ConfigureApplication ) ;
4650
47- Logging = new LoggingBuilder ( Services ) ;
48- WebHost = _deferredWebHostBuilder = new ConfigureWebHostBuilder ( Configuration , _environment , Services ) ;
49- Host = _deferredHostBuilder = new ConfigureHostBuilder ( Configuration , _environment , Services ) ;
51+ // We need to override the application name since the call to Configure will set it to
52+ // be the calling assembly's name.
53+ webHostBuilder . UseSetting ( WebHostDefaults . ApplicationKey , _environment . ApplicationName ) ;
5054
51- // Add default services
52- _deferredHostBuilder . ConfigureDefaults ( args ) ;
55+ // Store this so we can apply settings from _environment during build.
56+ _capturedWebHostBuilder = webHostBuilder ;
57+ } ) ;
58+ _bootstrapHostBuilder . RunDefaultCallbacks ( _hostBuilder ) ;
5359
54- // Enable changes here because we need to pick up configuration sources added by the generic web host
55- _deferredHostBuilder . ConfigurationEnabled = true ;
56- _deferredHostBuilder . ConfigureWebHostDefaults ( configure : _ => { } ) ;
60+ Logging = new LoggingBuilder ( Services ) ;
61+ WebHost = new ConfigureWebHostBuilder ( Configuration , _environment , Services ) ;
62+ Host = new ConfigureHostBuilder ( Configuration , _environment , Services ) ;
5763
5864 // This is important because GenericWebHostBuilder does the following and we want to preserve the WebHostBuilderContext:
5965 // context.Properties[typeof(WebHostBuilderContext)] = webHostBuilderContext;
6066 // context.Properties[typeof(WebHostOptions)] = options;
61- foreach ( var ( key , value ) in _deferredHostBuilder . Properties )
67+ foreach ( var ( key , value ) in _bootstrapHostBuilder . Properties )
6268 {
6369 _hostBuilder . Properties [ key ] = value ;
6470 }
@@ -102,6 +108,9 @@ internal WebApplicationBuilder(Assembly? callingAssembly, string[]? args = null)
102108 /// <returns>A configured <see cref="WebApplication"/>.</returns>
103109 public WebApplication Build ( )
104110 {
111+ // Apply changes made to WebApplicationBuilder.Environment directly before building.
112+ _environment . ApplyEnvironmentSettings ( _capturedWebHostBuilder , _hostBuilder ) ;
113+
105114 // Copy the configuration sources into the final IConfigurationBuilder
106115 _hostBuilder . ConfigureHostConfiguration ( builder =>
107116 {
@@ -116,10 +125,36 @@ public WebApplication Build()
116125 }
117126 } ) ;
118127
119- // We call ConfigureWebHostDefaults AGAIN because config might be added like "ForwardedHeaders_Enabled"
120- // which can add even more services. If not for that, we probably call _hostBuilder.ConfigureWebHost(ConfigureWebHost)
121- // instead in order to avoid duplicate service registration.
122- _hostBuilder . ConfigureWebHostDefaults ( ConfigureWebHost ) ;
128+ // This needs to go here to avoid adding the IHostedService that boots the server twice (the GenericWebHostService).
129+ // Copy the services that were added via WebApplicationBuilder.Services into the final IServiceCollection
130+ _hostBuilder . ConfigureServices ( ( context , services ) =>
131+ {
132+ // We've only added services configured by the GenericWebHostBuilder and WebHost.ConfigureWebDefaults
133+ // at this point. HostBuilder news up a new ServiceCollection in HostBuilder.Build() we haven't seen
134+ // until now, so we cannot clear these services even though some are redundant because
135+ // we called ConfigureWebHostDefaults on both the _deferredHostBuilder and _hostBuilder.
136+
137+ // Ideally, we'd only call _hostBuilder.ConfigureWebHost(ConfigureWebHost) instead of
138+ // _hostBuilder.ConfigureWebHostDefaults(ConfigureWebHost) to avoid some duplicate service descriptors,
139+ // but we want to add services in the WebApplicationBuilder constructor so code can inspect
140+ // WebApplicationBuilder.Services. At the same time, we want to be able which services are loaded
141+ // to react to config changes (e.g. ForwardedHeadersStartupFilter).
142+ foreach ( var s in _services )
143+ {
144+ services . Add ( s ) ;
145+ }
146+
147+ // Add any services to the user visible service collection so that they are observable
148+ // just in case users capture the Services property. Orchard does this to get a "blueprint"
149+ // of the service collection
150+
151+ // Drop the reference to the existing collection and set the inner collection
152+ // to the new one. This allows code that has references to the service collection to still function.
153+ _services . InnerCollection = services ;
154+ } ) ;
155+
156+ // Run the other callbacks on the final host builder
157+ Host . RunDeferredCallbacks ( _hostBuilder ) ;
123158
124159 _builtApplication = new WebApplication ( _hostBuilder . Build ( ) ) ;
125160
@@ -202,44 +237,6 @@ private void ConfigureApplication(WebHostBuilderContext context, IApplicationBui
202237 }
203238 }
204239
205- private void ConfigureWebHost ( IWebHostBuilder genericWebHostBuilder )
206- {
207- genericWebHostBuilder . Configure ( ConfigureApplication ) ;
208-
209- // This needs to go here to avoid adding the IHostedService that boots the server twice (the GenericWebHostService).
210- // Copy the services that were added via WebApplicationBuilder.Services into the final IServiceCollection
211- genericWebHostBuilder . ConfigureServices ( ( context , services ) =>
212- {
213- // We've only added services configured by the GenericWebHostBuilder and WebHost.ConfigureWebDefaults
214- // at this point. HostBuilder news up a new ServiceCollection in HostBuilder.Build() we haven't seen
215- // until now, so we cannot clear these services even though some are redundant because
216- // we called ConfigureWebHostDefaults on both the _deferredHostBuilder and _hostBuilder.
217-
218- // Ideally, we'd only call _hostBuilder.ConfigureWebHost(ConfigureWebHost) instead of
219- // _hostBuilder.ConfigureWebHostDefaults(ConfigureWebHost) to avoid some duplicate service descriptors,
220- // but we want to add services in the WebApplicationBuilder constructor so code can inspect
221- // WebApplicationBuilder.Services. At the same time, we want to be able which services are loaded
222- // to react to config changes (e.g. ForwardedHeadersStartupFilter).
223- foreach ( var s in _services )
224- {
225- services . Add ( s ) ;
226- }
227-
228- // Add any services to the user visible service collection so that they are observable
229- // just in case users capture the Services property. Orchard does this to get a "blueprint"
230- // of the service collection
231-
232- // Drop the reference to the existing collection and set the inner collection
233- // to the new one. This allows code that has references to the service collection to still function.
234- _services . InnerCollection = services ;
235- } ) ;
236-
237- // Run the other callbacks on the final host builder
238- _deferredHostBuilder . RunDeferredCallbacks ( _hostBuilder ) ;
239-
240- _environment . ApplyEnvironmentSettings ( genericWebHostBuilder ) ;
241- }
242-
243240 private static IEndpointRouteBuilder ? GetEndpointRouteBuilder ( IApplicationBuilder app )
244241 {
245242 app . Properties . TryGetValue ( EndpointRouteBuilderKey , out var value ) ;
0 commit comments