2
2
// The .NET Foundation licenses this file to you under the MIT license.
3
3
4
4
using System . Diagnostics ;
5
+ using System . Linq ;
5
6
using Microsoft . AspNetCore . Hosting ;
6
7
using Microsoft . Extensions . Configuration ;
7
8
using Microsoft . Extensions . DependencyInjection ;
@@ -15,11 +16,12 @@ namespace Microsoft.AspNetCore.Builder
15
16
/// </summary>
16
17
public sealed class WebApplicationBuilder
17
18
{
19
+ private const string EndpointRouteBuilderKey = "__EndpointRouteBuilder" ;
20
+
18
21
private readonly HostBuilder _hostBuilder = new ( ) ;
19
22
private readonly BootstrapHostBuilder _bootstrapHostBuilder ;
20
23
private readonly WebApplicationServiceCollection _services = new ( ) ;
21
24
private readonly List < KeyValuePair < string , string > > _hostConfigurationValues ;
22
- private const string EndpointRouteBuilderKey = "__EndpointRouteBuilder" ;
23
25
24
26
private WebApplication ? _builtApplication ;
25
27
@@ -62,7 +64,6 @@ internal WebApplicationBuilder(WebApplicationOptions options, Action<IHostBuilde
62
64
} ) ;
63
65
64
66
// Apply the args to host configuration last since ConfigureWebHostDefaults overrides a host specific setting (the application name).
65
-
66
67
_bootstrapHostBuilder . ConfigureHostConfiguration ( config =>
67
68
{
68
69
if ( args is { Length : > 0 } )
@@ -74,7 +75,6 @@ internal WebApplicationBuilder(WebApplicationOptions options, Action<IHostBuilde
74
75
options . ApplyHostConfiguration ( config ) ;
75
76
} ) ;
76
77
77
-
78
78
Configuration = new ( ) ;
79
79
80
80
// 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
100
100
Host = new ConfigureHostBuilder ( hostContext , Configuration , Services ) ;
101
101
WebHost = new ConfigureWebHostBuilder ( webHostContext , Configuration , Services ) ;
102
102
103
- Services . AddSingleton < IConfiguration > ( Configuration ) ;
103
+ Services . AddSingleton < IConfiguration > ( _ => Configuration ) ;
104
104
}
105
105
106
106
/// <summary>
@@ -148,14 +148,13 @@ public WebApplication Build()
148
148
builder . AddInMemoryCollection ( _hostConfigurationValues ) ;
149
149
} ) ;
150
150
151
- // Wire up the application configuration by copying the already built configuration providers over to final configuration builder.
152
- // We wrap the existing provider in a configuration source to avoid re-bulding the already added configuration sources.
151
+ // Wire up the _hostBuilder's application configuration with a ChainedConfigurationSource to the ConfigurationManager.
152
+ // We use a "tracking" source to avoid creating a circular reference when copying providers in ConfigureServices.
153
+ var chainedConfigSource = new TrackingChainedConfigurationSource ( Configuration ) ;
154
+
153
155
_hostBuilder . ConfigureAppConfiguration ( builder =>
154
156
{
155
- foreach ( var provider in ( ( IConfigurationRoot ) Configuration ) . Providers )
156
- {
157
- builder . Sources . Add ( new ConfigurationProviderSource ( provider ) ) ;
158
- }
157
+ builder . Add ( chainedConfigSource ) ;
159
158
160
159
foreach ( var ( key , value ) in ( ( IConfigurationBuilder ) Configuration ) . Properties )
161
160
{
@@ -173,17 +172,6 @@ public WebApplication Build()
173
172
// we called ConfigureWebHostDefaults on both the _deferredHostBuilder and _hostBuilder.
174
173
foreach ( var s in _services )
175
174
{
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
-
187
175
services . Add ( s ) ;
188
176
}
189
177
@@ -205,21 +193,41 @@ public WebApplication Build()
205
193
// Drop the reference to the existing collection and set the inner collection
206
194
// to the new one. This allows code that has references to the service collection to still function.
207
195
_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 the ConfigurationManager match the final _hostBuilder's configuration. To do that, we add the additional providers
207
+ // to the inner _hostBuilders's configuration to the ConfigurationManager. We wrap the existing provider in a
208
+ // configuration source to avoid rebulding or reloading the already added configuration sources.
209
+ foreach ( var provider in hostBuilderProviders )
210
+ {
211
+ // Avoid creating a circular reference to the ConfigurationManager via the chained configuration source.
212
+ if ( ! ReferenceEquals ( provider , chainedConfigSource . BuiltProvider ) )
213
+ {
214
+ ( ( IConfigurationBuilder ) Configuration ) . Add ( new ConfigurationProviderSource ( provider ) ) ;
215
+ }
216
+ }
208
217
} ) ;
209
218
210
219
// Run the other callbacks on the final host builder
211
220
Host . RunDeferredCallbacks ( _hostBuilder ) ;
212
221
213
222
_builtApplication = new WebApplication ( _hostBuilder . Build ( ) ) ;
214
223
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
-
220
224
// Mark the service collection as read-only to prevent future modifications
221
225
_services . IsReadOnly = true ;
222
226
227
+ // Resolve both the _hostBuilder's Configuration and builder.Configuration to mark both as resolved within the
228
+ // service provider ensuring both will be properly disposed with the provider.
229
+ _ = _builtApplication . Services . GetService < IEnumerable < IConfiguration > > ( ) ;
230
+
223
231
return _builtApplication ;
224
232
}
225
233
@@ -300,20 +308,5 @@ public LoggingBuilder(IServiceCollection services)
300
308
301
309
public IServiceCollection Services { get ; }
302
310
}
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
- }
318
311
}
319
312
}
0 commit comments