@@ -20,10 +20,17 @@ public sealed class WebApplicationBuilder
2020 private const string EndpointRouteBuilderKey = "__EndpointRouteBuilder" ;
2121
2222 private readonly HostBuilder _hostBuilder = new ( ) ;
23+
24+ private readonly BootstrapHostBuilder _bootstrapHostBuilder ;
2325 private readonly ConfigureHostBuilder _deferredHostBuilder ;
2426 private readonly ConfigureWebHostBuilder _deferredWebHostBuilder ;
27+
2528 private readonly WebHostEnvironment _environment ;
2629 private readonly WebApplicationServiceCollection _services = new ( ) ;
30+
31+ // This is effectively readonly because it is always set inline by the ConfigureWebHostDefaults callback in the ctor.
32+ private IWebHostBuilder _bootstrapWebHostBuilder = default ! ;
33+
2734 private WebApplication ? _builtApplication ;
2835
2936 internal WebApplicationBuilder ( Assembly ? callingAssembly , string [ ] ? args = null )
@@ -34,31 +41,27 @@ internal WebApplicationBuilder(Assembly? callingAssembly, string[]? args = null)
3441 Environment = _environment = new WebHostEnvironment ( callingAssembly ) ;
3542
3643 Configuration . SetBasePath ( _environment . ContentRootPath ) ;
37- Services . AddSingleton ( Environment ) ;
3844
3945 // Run methods to configure both generic and web host defaults early to populate config from appsettings.json
4046 // environment variables (both DOTNET_ and ASPNETCORE_ prefixed) and other possible default sources to prepopulate
4147 // the correct defaults.
42- var bootstrapBuilder = new BootstrapHostBuilder ( Configuration , _environment ) ;
43- bootstrapBuilder . ConfigureDefaults ( args ) ;
44- bootstrapBuilder . ConfigureWebHostDefaults ( configure : _ => { } ) ;
45- bootstrapBuilder . RunConfigurationCallbacks ( ) ;
48+ _bootstrapHostBuilder = new BootstrapHostBuilder ( Configuration , _environment , Services ) ;
49+ _bootstrapHostBuilder . ConfigureDefaults ( args ) ;
50+ _bootstrapHostBuilder . ConfigureWebHostDefaults ( webHostBuilder =>
51+ {
52+ // This runs inline.
53+ _bootstrapWebHostBuilder = webHostBuilder ;
54+ } ) ;
55+ _bootstrapHostBuilder . RunDefaultCallbacks ( ) ;
4656
4757 Logging = new LoggingBuilder ( Services ) ;
4858 WebHost = _deferredWebHostBuilder = new ConfigureWebHostBuilder ( Configuration , _environment , Services ) ;
4959 Host = _deferredHostBuilder = new ConfigureHostBuilder ( Configuration , _environment , Services ) ;
5060
51- // Add default services
52- _deferredHostBuilder . ConfigureDefaults ( args ) ;
53-
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 : _ => { } ) ;
57-
5861 // This is important because GenericWebHostBuilder does the following and we want to preserve the WebHostBuilderContext:
5962 // context.Properties[typeof(WebHostBuilderContext)] = webHostBuilderContext;
6063 // context.Properties[typeof(WebHostOptions)] = options;
61- foreach ( var ( key , value ) in _deferredHostBuilder . Properties )
64+ foreach ( var ( key , value ) in _bootstrapHostBuilder . Properties )
6265 {
6366 _hostBuilder . Properties [ key ] = value ;
6467 }
@@ -116,10 +119,42 @@ public WebApplication Build()
116119 }
117120 } ) ;
118121
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 ) ;
122+ // Configure the original GenericWebHostBuilder that added the default services we use.
123+ _bootstrapWebHostBuilder . Configure ( ConfigureApplication ) ;
124+ _environment . ApplyEnvironmentSettings ( _bootstrapWebHostBuilder ) ;
125+ // This has further modified the _bootstrapHostBuilder, so we need to run the newly added callbacks in the proper order.
126+ _bootstrapHostBuilder . RunDelayedCallbacks ( _hostBuilder ) ;
127+
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+ _deferredHostBuilder . 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