Skip to content

Commit 3440cd3

Browse files
committed
Replace IgnoreLoadConfigurationProviders after build
1 parent 71f9884 commit 3440cd3

File tree

3 files changed

+71
-44
lines changed

3 files changed

+71
-44
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Microsoft.Extensions.Configuration;
5+
using Microsoft.Extensions.Primitives;
6+
7+
namespace Microsoft.AspNetCore;
8+
9+
internal sealed class IgnoreLoadConfigurationProvider : IConfigurationProvider, IDisposable
10+
{
11+
private readonly IConfigurationProvider _configurationProvider;
12+
13+
public IgnoreLoadConfigurationProvider(IConfigurationProvider configurationProvider)
14+
{
15+
_configurationProvider = configurationProvider;
16+
}
17+
18+
public IConfigurationProvider OriginalProvider => _configurationProvider;
19+
20+
public void Load()
21+
{
22+
// ConfigurationManager has already loaded its IConfigurationProviders, so we do not need to load it again
23+
// during WebApplicationBuilder.Build(). See https://github.com/dotnet/aspnetcore/issues/37030
24+
// This is replaced by OriginalProvider at the end of ApplicationBuilder.Build().
25+
}
26+
27+
public IChangeToken GetReloadToken() => _configurationProvider.GetReloadToken();
28+
29+
public IEnumerable<string> GetChildKeys(IEnumerable<string> earlierKeys, string parentPath) =>
30+
_configurationProvider.GetChildKeys(earlierKeys, parentPath);
31+
32+
public void Set(string key, string value) => _configurationProvider.Set(key, value);
33+
34+
public bool TryGet(string key, out string value) => _configurationProvider.TryGet(key, out value);
35+
36+
public override string ToString() => _configurationProvider.ToString()!;
37+
38+
public override bool Equals(object? obj) => _configurationProvider.Equals(obj);
39+
40+
public override int GetHashCode() => _configurationProvider.GetHashCode();
41+
42+
public void Dispose() => (_configurationProvider as IDisposable)?.Dispose();
43+
}

src/DefaultBuilder/src/WebApplicationBuilder.cs

Lines changed: 16 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using Microsoft.Extensions.DependencyInjection;
88
using Microsoft.Extensions.Hosting;
99
using Microsoft.Extensions.Logging;
10-
using Microsoft.Extensions.Primitives;
1110

1211
namespace Microsoft.AspNetCore.Builder
1312
{
@@ -16,11 +15,12 @@ namespace Microsoft.AspNetCore.Builder
1615
/// </summary>
1716
public sealed class WebApplicationBuilder
1817
{
18+
private const string EndpointRouteBuilderKey = "__EndpointRouteBuilder";
19+
1920
private readonly HostBuilder _hostBuilder = new();
2021
private readonly BootstrapHostBuilder _bootstrapHostBuilder;
2122
private readonly WebApplicationServiceCollection _services = new();
2223
private readonly List<KeyValuePair<string, string>> _hostConfigurationValues;
23-
private const string EndpointRouteBuilderKey = "__EndpointRouteBuilder";
2424

2525
private WebApplication? _builtApplication;
2626

@@ -213,8 +213,20 @@ public WebApplication Build()
213213

214214
_builtApplication = new WebApplication(_hostBuilder.Build());
215215

216+
// We know that ConfigurationBuilder constructs ConfigurationRoot with a List<IConfigurationProvider> and all the
217+
// IgnoreLoadConfigurationProviders have been "loaded", so we can put the originals back.
218+
var providerList = (List<IConfigurationProvider>)((IConfigurationRoot)_builtApplication.Configuration).Providers;
219+
for (var i = 0; i < providerList.Count; i++)
220+
{
221+
if (providerList[i] is IgnoreLoadConfigurationProvider wrappedProvider)
222+
{
223+
providerList[i] = wrappedProvider.OriginalProvider;
224+
}
225+
}
226+
216227
// Make builder.Configuration match the final configuration. To do that
217-
// we clear the sources and add the built configuration as a source
228+
// we recreate the ConfigurationManager and add the built configuration as a source.
229+
// We do not clear or dispose the old ConfigurationManager as this would dispose IConfigurationProviders that are in use.
218230
Configuration = new ConfigurationManager();
219231
Configuration.AddConfiguration(_builtApplication.Configuration);
220232

@@ -310,50 +322,10 @@ public ConfigurationProviderSource(IConfigurationProvider configurationProvider)
310322
{
311323
// ConfigurationManager has already loaded its IConfigurationProviders, so we do not need to load it again
312324
// during WebApplicationBuilder.Build(). See https://github.com/dotnet/aspnetcore/issues/37030
313-
_configurationProvider = new IgnoreFirstLoadConfigurationProvider(configurationProvider);
325+
_configurationProvider = new IgnoreLoadConfigurationProvider(configurationProvider);
314326
}
315327

316328
public IConfigurationProvider Build(IConfigurationBuilder builder) => _configurationProvider;
317-
318-
private sealed class IgnoreFirstLoadConfigurationProvider : IConfigurationProvider, IDisposable
319-
{
320-
private readonly IConfigurationProvider _configurationProvider;
321-
322-
private bool _hasIgnoredFirstLoad;
323-
324-
public IgnoreFirstLoadConfigurationProvider(IConfigurationProvider configurationProvider)
325-
{
326-
_configurationProvider = configurationProvider;
327-
}
328-
329-
public void Load()
330-
{
331-
if (!_hasIgnoredFirstLoad)
332-
{
333-
_hasIgnoredFirstLoad = true;
334-
return;
335-
}
336-
337-
_configurationProvider.Load();
338-
}
339-
340-
public IChangeToken GetReloadToken() => _configurationProvider.GetReloadToken();
341-
342-
public IEnumerable<string> GetChildKeys(IEnumerable<string> earlierKeys, string parentPath) =>
343-
_configurationProvider.GetChildKeys(earlierKeys, parentPath);
344-
345-
public void Set(string key, string value) => _configurationProvider.Set(key, value);
346-
347-
public bool TryGet(string key, out string value) => _configurationProvider.TryGet(key, out value);
348-
349-
public void Dispose() => (_configurationProvider as IDisposable)?.Dispose();
350-
351-
public override string ToString() => _configurationProvider.ToString()!;
352-
353-
public override bool Equals(object? obj) => _configurationProvider.Equals(obj);
354-
355-
public override int GetHashCode() => _configurationProvider.GetHashCode();
356-
}
357329
}
358330
}
359331
}

src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1590,6 +1590,18 @@ public void ConfigurationProvidersAreDisposedWithWebApplication()
15901590
Assert.Equal(1, configSource.ProvidersDisposed);
15911591
}
15921592

1593+
[Fact]
1594+
public void ConfigurationProviderTypesArePreserved()
1595+
{
1596+
var builder = WebApplication.CreateBuilder();
1597+
1598+
((IConfigurationBuilder)builder.Configuration).Sources.Add(new RandomConfigurationSource());
1599+
1600+
var app = builder.Build();
1601+
1602+
Assert.Single(((IConfigurationRoot)app.Configuration).Providers.OfType<RandomConfigurationProvider>());
1603+
}
1604+
15931605
public class RandomConfigurationSource : IConfigurationSource
15941606
{
15951607
public int ProvidersBuilt { get; set; }

0 commit comments

Comments
 (0)