@@ -22,6 +22,7 @@ public sealed class WebApplicationBuilder
2222 private readonly BootstrapHostBuilder _bootstrapHostBuilder ;
2323 private readonly WebApplicationServiceCollection _services = new ( ) ;
2424 private readonly List < KeyValuePair < string , string > > _hostConfigurationValues ;
25+ private readonly ConfigurationManager _hostConfigurationManager = new ( ) ;
2526
2627 private WebApplication ? _builtApplication ;
2728
@@ -76,6 +77,8 @@ internal WebApplicationBuilder(WebApplicationOptions options, Action<IHostBuilde
7677 } ) ;
7778
7879 Configuration = new ( ) ;
80+ // This is chained as the first configuration source in Configuration so host config can be added later without overriding app config.
81+ Configuration . AddConfiguration ( _hostConfigurationManager , shouldDisposeConfiguration : true ) ;
7982
8083 // Collect the hosted services separately since we want those to run after the user's hosted services
8184 _services . TrackHostedServices = true ;
@@ -194,22 +197,32 @@ public WebApplication Build()
194197 // to the new one. This allows code that has references to the service collection to still function.
195198 _services . InnerCollection = services ;
196199
200+ // Keep any configuration sources added before the TrackingChainedConfigurationSource (namely host configuration from _hostConfigurationValues)
201+ // from overriding config values set via Configuration by inserting them at beginning using _hostConfigurationValues.
202+ var beforeChainedConfig = true ;
197203 var hostBuilderProviders = ( ( IConfigurationRoot ) context . Configuration ) . Providers ;
198204
199205 if ( ! hostBuilderProviders . Contains ( chainedConfigSource . BuiltProvider ) )
200206 {
201207 // Something removed the _hostBuilder's TrackingChainedConfigurationSource pointing back to the ConfigurationManager.
202208 // This is likely a test using WebApplicationFactory. Replicate the effect by clearing the ConfingurationManager sources.
203209 ( ( IConfigurationBuilder ) Configuration ) . Sources . Clear ( ) ;
210+ beforeChainedConfig = false ;
204211 }
205212
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.
213+ // Make the ConfigurationManager match the final _hostBuilder's configuration. To do that, we add the additional providers
214+ // to the inner _hostBuilders's configuration to the ConfigurationManager. We wrap the existing provider in a
215+ // configuration source to avoid rebuilding or reloading the already added configuration sources.
208216 foreach ( var provider in hostBuilderProviders )
209217 {
210- if ( ! ReferenceEquals ( provider , chainedConfigSource . BuiltProvider ) )
218+ if ( ReferenceEquals ( provider , chainedConfigSource . BuiltProvider ) )
211219 {
212- ( ( IConfigurationBuilder ) Configuration ) . Add ( new ConfigurationProviderSource ( provider ) ) ;
220+ beforeChainedConfig = false ;
221+ }
222+ else
223+ {
224+ IConfigurationBuilder configBuilder = beforeChainedConfig ? _hostConfigurationManager : Configuration ;
225+ configBuilder . Add ( new ConfigurationProviderSource ( provider ) ) ;
213226 }
214227 }
215228 } ) ;
0 commit comments