diff --git a/src/DefaultBuilder/src/WebApplicationBuilder.cs b/src/DefaultBuilder/src/WebApplicationBuilder.cs index 142761840125..43c818443e25 100644 --- a/src/DefaultBuilder/src/WebApplicationBuilder.cs +++ b/src/DefaultBuilder/src/WebApplicationBuilder.cs @@ -205,6 +205,16 @@ private void ConfigureWebHost(IWebHostBuilder genericWebHostBuilder) { services.Add(s); } + + // Add any services to the user visible service collection so that they are observable + // just in case users capture the Services property. Orchard does this to get a "blueprint" + // of the service collection. The order needs to be preserved here so we clear the original + // collection and add all of the services in order. + Services.Clear(); + foreach (var s in services) + { + Services.Add(s); + } }); genericWebHostBuilder.Configure(ConfigureApplication); diff --git a/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs b/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs index 66769d18e17a..bfe14a4876f6 100644 --- a/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs +++ b/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs @@ -269,6 +269,52 @@ public void WebApplicationCreate_RegistersRouting() Assert.NotNull(linkGenerator); } + [Fact] + public void WebApplication_CanResolveDefaultServicesFromServiceCollection() + { + var builder = WebApplication.CreateBuilder(); + + // Add the service collection to the service collection + builder.Services.AddSingleton(builder.Services); + + var app = builder.Build(); + + var env0 = app.Services.GetRequiredService(); + + var env1 = app.Services.GetRequiredService().BuildServiceProvider().GetRequiredService(); + + Assert.Equal(env0.ApplicationName, env1.ApplicationName); + Assert.Equal(env0.EnvironmentName, env1.EnvironmentName); + Assert.Equal(env0.ContentRootPath, env1.ContentRootPath); + } + + [Fact] + public void WebApplication_CanResolveDefaultServicesFromServiceCollectionInCorrectOrder() + { + var builder = WebApplication.CreateBuilder(); + + // Add the service collection to the service collection + builder.Services.AddSingleton(builder.Services); + + // We're overriding the default IHostLifetime so that we can test the order in which it's resolved. + // This should override the default IHostLifetime. + builder.Services.AddSingleton(); + + var app = builder.Build(); + + var hostLifetime0 = app.Services.GetRequiredService(); + var childServiceProvider = app.Services.GetRequiredService().BuildServiceProvider(); + var hostLifetime1 = childServiceProvider.GetRequiredService(); + + var hostLifetimes0 = app.Services.GetServices().ToArray(); + var hostLifetimes1 = childServiceProvider.GetServices().ToArray(); + + Assert.IsType(hostLifetime0); + Assert.IsType(hostLifetime1); + + Assert.Equal(hostLifetimes1.Length, hostLifetimes0.Length); + } + [Fact] public void WebApplicationCreate_RegistersEventSourceLogger() { @@ -328,6 +374,19 @@ public void WebApplicationBuilder_CanSetWebRootPaths(bool useSetter) Assert.Equal(fullWebRootPath, app.Environment.WebRootPath); } + private class CustomHostLifetime : IHostLifetime + { + public Task StopAsync(CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public Task WaitForStartAsync(CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + } + private class TestEventListener : EventListener { private volatile bool _disposed;