Skip to content

Commit 3ae4729

Browse files
authored
Avoid building the configuration sources twice (#36570)
- When making the internal host builder, we copy the sources from the ConfigurationManager to the internal IConfigurationBuilder and the HostBuilder creates. This results in building configuration twice which can be problematic for performance reasons. Instead, we copy over the already built configuration providers in a custom IConfigurationSource implementation. This change does means the looking at the list of sources will not match the original list of sources though. - Added a test
1 parent 44b7bfa commit 3ae4729

File tree

3 files changed

+39
-7
lines changed

3 files changed

+39
-7
lines changed

src/DefaultBuilder/DefaultBuilder.slnf

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"src\\Hosting\\Hosting\\src\\Microsoft.AspNetCore.Hosting.csproj",
1212
"src\\Hosting\\Server.Abstractions\\src\\Microsoft.AspNetCore.Hosting.Server.Abstractions.csproj",
1313
"src\\Hosting\\Server.IntegrationTesting\\src\\Microsoft.AspNetCore.Server.IntegrationTesting.csproj",
14+
"src\\Hosting\\TestHost\\src\\Microsoft.AspNetCore.TestHost.csproj",
1415
"src\\Http\\Authentication.Abstractions\\src\\Microsoft.AspNetCore.Authentication.Abstractions.csproj",
1516
"src\\Http\\Authentication.Core\\src\\Microsoft.AspNetCore.Authentication.Core.csproj",
1617
"src\\Http\\Headers\\src\\Microsoft.Net.Http.Headers.csproj",
@@ -35,7 +36,8 @@
3536
"src\\Servers\\Kestrel\\Core\\src\\Microsoft.AspNetCore.Server.Kestrel.Core.csproj",
3637
"src\\Servers\\Kestrel\\Kestrel\\src\\Microsoft.AspNetCore.Server.Kestrel.csproj",
3738
"src\\Servers\\Kestrel\\Transport.Quic\\src\\Microsoft.AspNetCore.Server.Kestrel.Transport.Quic.csproj",
38-
"src\\Servers\\Kestrel\\Transport.Sockets\\src\\Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj"
39+
"src\\Servers\\Kestrel\\Transport.Sockets\\src\\Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj",
40+
"src\\Testing\\src\\Microsoft.AspNetCore.Testing.csproj"
3941
]
4042
}
4143
}

src/DefaultBuilder/src/WebApplicationBuilder.cs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -146,14 +146,13 @@ public WebApplication Build()
146146
builder.AddInMemoryCollection(_hostConfigurationValues);
147147
});
148148

149-
// Wire up the application configuration by copying the sources over to final configuration builder.
150-
// this will also contain host configuration since it's chained (unless the sources were cleared)
151-
// but it can't affect the hosting configuration at this point so it's harmless.
149+
// Wire up the application configuration by copying the already built configuration providers over to final configuration builder.
150+
// We wrap the existing provider in a configuration source to avoid re-bulding the already added configuration sources.
152151
_hostBuilder.ConfigureAppConfiguration(builder =>
153152
{
154-
foreach (var source in ((IConfigurationBuilder)Configuration).Sources)
153+
foreach (var provider in ((IConfigurationRoot)Configuration).Providers)
155154
{
156-
builder.Sources.Add(source);
155+
builder.Sources.Add(new ConfigurationProviderSource(provider));
157156
}
158157

159158
foreach (var (key, value) in ((IConfigurationBuilder)Configuration).Properties)
@@ -279,7 +278,7 @@ private void ConfigureApplication(WebHostBuilderContext context, IApplicationBui
279278
}
280279
}
281280

282-
private class LoggingBuilder : ILoggingBuilder
281+
private sealed class LoggingBuilder : ILoggingBuilder
283282
{
284283
public LoggingBuilder(IServiceCollection services)
285284
{
@@ -288,5 +287,20 @@ public LoggingBuilder(IServiceCollection services)
288287

289288
public IServiceCollection Services { get; }
290289
}
290+
291+
private sealed class ConfigurationProviderSource : IConfigurationSource
292+
{
293+
private readonly IConfigurationProvider _configurationProvider;
294+
295+
public ConfigurationProviderSource(IConfigurationProvider configurationProvider)
296+
{
297+
_configurationProvider = configurationProvider;
298+
}
299+
300+
public IConfigurationProvider Build(IConfigurationBuilder builder)
301+
{
302+
return _configurationProvider;
303+
}
304+
}
291305
}
292306
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1478,10 +1478,26 @@ public void ConfigurationCanBeReloaded()
14781478
Assert.NotEqual(value0, value1);
14791479
}
14801480

1481+
[Fact]
1482+
public void ConfigurationSourcesAreBuiltOnce()
1483+
{
1484+
var builder = WebApplication.CreateBuilder();
1485+
1486+
var configSource = new RandomConfigurationSource();
1487+
((IConfigurationBuilder)builder.Configuration).Sources.Add(configSource);
1488+
1489+
var app = builder.Build();
1490+
1491+
Assert.Equal(1, configSource.ProvidersBuilt);
1492+
}
1493+
14811494
public class RandomConfigurationSource : IConfigurationSource
14821495
{
1496+
public int ProvidersBuilt { get; set; }
1497+
14831498
public IConfigurationProvider Build(IConfigurationBuilder builder)
14841499
{
1500+
ProvidersBuilt++;
14851501
return new RandomConfigurationProvider();
14861502
}
14871503
}

0 commit comments

Comments
 (0)