@@ -15,11 +15,12 @@ namespace Microsoft.AspNetCore.Builder
1515 /// </summary>
1616 public sealed class WebApplicationBuilder
1717 {
18+ private const string EndpointRouteBuilderKey = "__EndpointRouteBuilder" ;
19+
1820 private readonly HostBuilder _hostBuilder = new ( ) ;
1921 private readonly BootstrapHostBuilder _bootstrapHostBuilder ;
2022 private readonly WebApplicationServiceCollection _services = new ( ) ;
2123 private readonly List < KeyValuePair < string , string > > _hostConfigurationValues ;
22- private const string EndpointRouteBuilderKey = "__EndpointRouteBuilder" ;
2324
2425 private WebApplication ? _builtApplication ;
2526
@@ -62,7 +63,6 @@ internal WebApplicationBuilder(WebApplicationOptions options, Action<IHostBuilde
6263 } ) ;
6364
6465 // Apply the args to host configuration last since ConfigureWebHostDefaults overrides a host specific setting (the application name).
65-
6666 _bootstrapHostBuilder . ConfigureHostConfiguration ( config =>
6767 {
6868 if ( args is { Length : > 0 } )
@@ -74,7 +74,6 @@ internal WebApplicationBuilder(WebApplicationOptions options, Action<IHostBuilde
7474 options . ApplyHostConfiguration ( config ) ;
7575 } ) ;
7676
77-
7877 Configuration = new ( ) ;
7978
8079 // Collect the hosted services separately since we want those to run after the user's hosted services
@@ -100,7 +99,7 @@ internal WebApplicationBuilder(WebApplicationOptions options, Action<IHostBuilde
10099 Host = new ConfigureHostBuilder ( hostContext , Configuration , Services ) ;
101100 WebHost = new ConfigureWebHostBuilder ( webHostContext , Configuration , Services ) ;
102101
103- Services . AddSingleton < IConfiguration > ( Configuration ) ;
102+ Services . AddSingleton < IConfiguration > ( _ => Configuration ) ;
104103 }
105104
106105 /// <summary>
@@ -148,14 +147,13 @@ public WebApplication Build()
148147 builder . AddInMemoryCollection ( _hostConfigurationValues ) ;
149148 } ) ;
150149
150+ var chainedConfigSource = new TrackingChainedConfigurationSource ( Configuration ) ;
151+
151152 // Wire up the application configuration by copying the already built configuration providers over to final configuration builder.
152153 // We wrap the existing provider in a configuration source to avoid re-bulding the already added configuration sources.
153154 _hostBuilder . ConfigureAppConfiguration ( builder =>
154155 {
155- foreach ( var provider in ( ( IConfigurationRoot ) Configuration ) . Providers )
156- {
157- builder . Sources . Add ( new ConfigurationProviderSource ( provider ) ) ;
158- }
156+ builder . Add ( chainedConfigSource ) ;
159157
160158 foreach ( var ( key , value ) in ( ( IConfigurationBuilder ) Configuration ) . Properties )
161159 {
@@ -173,17 +171,6 @@ public WebApplication Build()
173171 // we called ConfigureWebHostDefaults on both the _deferredHostBuilder and _hostBuilder.
174172 foreach ( var s in _services )
175173 {
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-
187174 services . Add ( s ) ;
188175 }
189176
@@ -205,18 +192,23 @@ public WebApplication Build()
205192 // Drop the reference to the existing collection and set the inner collection
206193 // to the new one. This allows code that has references to the service collection to still function.
207194 _services . InnerCollection = services ;
195+
196+ // Make builder.Configuration match the final configuration. To do that, we add the additional
197+ // providers in the inner _hostBuilders's Configuration to the ConfigurationManager.
198+ foreach ( var provider in ( ( IConfigurationRoot ) context . Configuration ) . Providers )
199+ {
200+ if ( ! ReferenceEquals ( provider , chainedConfigSource . BuiltProvider ) )
201+ {
202+ ( ( IConfigurationBuilder ) Configuration ) . Add ( new ConfigurationProviderSource ( provider ) ) ;
203+ }
204+ }
208205 } ) ;
209206
210207 // Run the other callbacks on the final host builder
211208 Host . RunDeferredCallbacks ( _hostBuilder ) ;
212209
213210 _builtApplication = new WebApplication ( _hostBuilder . Build ( ) ) ;
214211
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-
220212 // Mark the service collection as read-only to prevent future modifications
221213 _services . IsReadOnly = true ;
222214
@@ -301,6 +293,24 @@ public LoggingBuilder(IServiceCollection services)
301293 public IServiceCollection Services { get ; }
302294 }
303295
296+ private sealed class TrackingChainedConfigurationSource : IConfigurationSource
297+ {
298+ private readonly ChainedConfigurationSource _chainedConfigurationSource = new ( ) ;
299+
300+ public TrackingChainedConfigurationSource ( ConfigurationManager configManager )
301+ {
302+ _chainedConfigurationSource . Configuration = configManager ;
303+ }
304+
305+ public IConfigurationProvider ? BuiltProvider { get ; set ; }
306+
307+ public IConfigurationProvider Build ( IConfigurationBuilder builder )
308+ {
309+ BuiltProvider = _chainedConfigurationSource . Build ( builder ) ;
310+ return BuiltProvider ;
311+ }
312+ }
313+
304314 private sealed class ConfigurationProviderSource : IConfigurationSource
305315 {
306316 private readonly IConfigurationProvider _configurationProvider ;
0 commit comments