@@ -22,6 +22,7 @@ public sealed class WebApplicationBuilder
22
22
private readonly BootstrapHostBuilder _bootstrapHostBuilder ;
23
23
private readonly WebApplicationServiceCollection _services = new ( ) ;
24
24
private readonly List < KeyValuePair < string , string > > _hostConfigurationValues ;
25
+ private readonly ConfigurationManager _hostConfigurationManager = new ( ) ;
25
26
26
27
private WebApplication ? _builtApplication ;
27
28
@@ -76,6 +77,8 @@ internal WebApplicationBuilder(WebApplicationOptions options, Action<IHostBuilde
76
77
} ) ;
77
78
78
79
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 ) ;
79
82
80
83
// Collect the hosted services separately since we want those to run after the user's hosted services
81
84
_services . TrackHostedServices = true ;
@@ -194,22 +197,32 @@ public WebApplication Build()
194
197
// to the new one. This allows code that has references to the service collection to still function.
195
198
_services . InnerCollection = services ;
196
199
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 ;
197
203
var hostBuilderProviders = ( ( IConfigurationRoot ) context . Configuration ) . Providers ;
198
204
199
205
if ( ! hostBuilderProviders . Contains ( chainedConfigSource . BuiltProvider ) )
200
206
{
201
207
// Something removed the _hostBuilder's TrackingChainedConfigurationSource pointing back to the ConfigurationManager.
202
208
// This is likely a test using WebApplicationFactory. Replicate the effect by clearing the ConfingurationManager sources.
203
209
( ( IConfigurationBuilder ) Configuration ) . Sources . Clear ( ) ;
210
+ beforeChainedConfig = false ;
204
211
}
205
212
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.
208
216
foreach ( var provider in hostBuilderProviders )
209
217
{
210
- if ( ! ReferenceEquals ( provider , chainedConfigSource . BuiltProvider ) )
218
+ if ( ReferenceEquals ( provider , chainedConfigSource . BuiltProvider ) )
211
219
{
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 ) ) ;
213
226
}
214
227
}
215
228
} ) ;
0 commit comments