22// The .NET Foundation licenses this file to you under the MIT license.
33
44using System . Diagnostics ;
5+ using System . Linq ;
56using Microsoft . AspNetCore . Hosting ;
67using Microsoft . Extensions . Configuration ;
78using Microsoft . Extensions . DependencyInjection ;
@@ -15,11 +16,12 @@ namespace Microsoft.AspNetCore.Builder
1516 /// </summary>
1617 public sealed class WebApplicationBuilder
1718 {
19+ private const string EndpointRouteBuilderKey = "__EndpointRouteBuilder" ;
20+
1821 private readonly HostBuilder _hostBuilder = new ( ) ;
1922 private readonly BootstrapHostBuilder _bootstrapHostBuilder ;
2023 private readonly WebApplicationServiceCollection _services = new ( ) ;
2124 private readonly List < KeyValuePair < string , string > > _hostConfigurationValues ;
22- private const string EndpointRouteBuilderKey = "__EndpointRouteBuilder" ;
2325
2426 private WebApplication ? _builtApplication ;
2527
@@ -62,7 +64,6 @@ internal WebApplicationBuilder(WebApplicationOptions options, Action<IHostBuilde
6264 } ) ;
6365
6466 // Apply the args to host configuration last since ConfigureWebHostDefaults overrides a host specific setting (the application name).
65-
6667 _bootstrapHostBuilder . ConfigureHostConfiguration ( config =>
6768 {
6869 if ( args is { Length : > 0 } )
@@ -74,7 +75,6 @@ internal WebApplicationBuilder(WebApplicationOptions options, Action<IHostBuilde
7475 options . ApplyHostConfiguration ( config ) ;
7576 } ) ;
7677
77-
7878 Configuration = new ( ) ;
7979
8080 // Collect the hosted services separately since we want those to run after the user's hosted services
@@ -100,7 +100,7 @@ internal WebApplicationBuilder(WebApplicationOptions options, Action<IHostBuilde
100100 Host = new ConfigureHostBuilder ( hostContext , Configuration , Services ) ;
101101 WebHost = new ConfigureWebHostBuilder ( webHostContext , Configuration , Services ) ;
102102
103- Services . AddSingleton < IConfiguration > ( Configuration ) ;
103+ Services . AddSingleton < IConfiguration > ( _ => Configuration ) ;
104104 }
105105
106106 /// <summary>
@@ -148,14 +148,13 @@ public WebApplication Build()
148148 builder . AddInMemoryCollection ( _hostConfigurationValues ) ;
149149 } ) ;
150150
151+ var chainedConfigSource = new TrackingChainedConfigurationSource ( Configuration ) ;
152+
151153 // Wire up the application configuration by copying the already built configuration providers over to final configuration builder.
152154 // We wrap the existing provider in a configuration source to avoid re-bulding the already added configuration sources.
153155 _hostBuilder . ConfigureAppConfiguration ( builder =>
154156 {
155- foreach ( var provider in ( ( IConfigurationRoot ) Configuration ) . Providers )
156- {
157- builder . Sources . Add ( new ConfigurationProviderSource ( provider ) ) ;
158- }
157+ builder . Add ( chainedConfigSource ) ;
159158
160159 foreach ( var ( key , value ) in ( ( IConfigurationBuilder ) Configuration ) . Properties )
161160 {
@@ -173,17 +172,6 @@ public WebApplication Build()
173172 // we called ConfigureWebHostDefaults on both the _deferredHostBuilder and _hostBuilder.
174173 foreach ( var s in _services )
175174 {
176- // Skip the configuration manager instance we added earlier
177- // we're already going to wire it up to this new configuration source
178- // after we've built the application. There's a chance the user manually added
179- // this as well but we still need to remove it from the final configuration
180- // to avoid cycles in the configuration graph
181- if ( s . ServiceType == typeof ( IConfiguration ) &&
182- s . ImplementationInstance == Configuration )
183- {
184- continue ;
185- }
186-
187175 services . Add ( s ) ;
188176 }
189177
@@ -205,21 +193,39 @@ public WebApplication Build()
205193 // Drop the reference to the existing collection and set the inner collection
206194 // to the new one. This allows code that has references to the service collection to still function.
207195 _services . InnerCollection = services ;
196+
197+ var hostBuilderProviders = ( ( IConfigurationRoot ) context . Configuration ) . Providers ;
198+
199+ if ( ! hostBuilderProviders . Contains ( chainedConfigSource . BuiltProvider ) )
200+ {
201+ // Something removed the _hostBuilder's TrackingChainedConfigurationSource pointing back to the ConfigurationManager.
202+ // This is likely a test using WebApplicationFactory. Replicate the effect by clearing the ConfingurationManager sources.
203+ ( ( IConfigurationBuilder ) Configuration ) . Sources . Clear ( ) ;
204+ }
205+
206+ // Make builder.Configuration match the final configuration. To do that, we add the additional
207+ // providers in the inner _hostBuilders's Configuration to the ConfigurationManager.
208+ foreach ( var provider in hostBuilderProviders )
209+ {
210+ if ( ! ReferenceEquals ( provider , chainedConfigSource . BuiltProvider ) )
211+ {
212+ ( ( IConfigurationBuilder ) Configuration ) . Add ( new ConfigurationProviderSource ( provider ) ) ;
213+ }
214+ }
208215 } ) ;
209216
210217 // Run the other callbacks on the final host builder
211218 Host . RunDeferredCallbacks ( _hostBuilder ) ;
212219
213220 _builtApplication = new WebApplication ( _hostBuilder . Build ( ) ) ;
214221
215- // Make builder.Configuration match the final configuration. To do that
216- // we clear the sources and add the built configuration as a source
217- ( ( IConfigurationBuilder ) Configuration ) . Sources . Clear ( ) ;
218- Configuration . AddConfiguration ( _builtApplication . Configuration ) ;
219-
220222 // Mark the service collection as read-only to prevent future modifications
221223 _services . IsReadOnly = true ;
222224
225+ // Resolve both the _hostBuilder's Configuration and builder.Configuration to mark both as resolved within the
226+ // service provider ensuring both will be properly disposed with the provider.
227+ _ = _builtApplication . Services . GetService < IEnumerable < IConfiguration > > ( ) ;
228+
223229 return _builtApplication ;
224230 }
225231
@@ -300,20 +306,5 @@ public LoggingBuilder(IServiceCollection services)
300306
301307 public IServiceCollection Services { get ; }
302308 }
303-
304- private sealed class ConfigurationProviderSource : IConfigurationSource
305- {
306- private readonly IConfigurationProvider _configurationProvider ;
307-
308- public ConfigurationProviderSource ( IConfigurationProvider configurationProvider )
309- {
310- _configurationProvider = configurationProvider ;
311- }
312-
313- public IConfigurationProvider Build ( IConfigurationBuilder builder )
314- {
315- return _configurationProvider ;
316- }
317- }
318309 }
319310}
0 commit comments