diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/HostApplicationBuilder.cs b/src/libraries/Microsoft.Extensions.Hosting/src/HostApplicationBuilder.cs index c26fdad6b74734..9962638c49d6aa 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/HostApplicationBuilder.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/HostApplicationBuilder.cs @@ -88,7 +88,12 @@ public HostApplicationBuilder(HostApplicationBuilderSettings? settings) if (!settings.DisableDefaults) { - HostingHostBuilderExtensions.ApplyDefaultHostConfiguration(Configuration, settings.Args); + if (settings.ContentRootPath is null && Configuration[HostDefaults.ContentRootKey] is null) + { + HostingHostBuilderExtensions.SetDefaultContentRoot(Configuration); + } + + HostingHostBuilderExtensions.AddDefaultHostConfigurationSources(Configuration, settings.Args); } // HostApplicationBuilderSettings override all other config sources. diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs b/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs index b8e8c207f650a3..5663e5fbb3c466 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs @@ -198,7 +198,13 @@ public static IHostBuilder ConfigureDefaults(this IHostBuilder builder, string[] .UseServiceProviderFactory(context => new DefaultServiceProviderFactory(CreateDefaultServiceProviderOptions(context))); } - internal static void ApplyDefaultHostConfiguration(IConfigurationBuilder hostConfigBuilder, string[]? args) + private static void ApplyDefaultHostConfiguration(IConfigurationBuilder hostConfigBuilder, string[]? args) + { + SetDefaultContentRoot(hostConfigBuilder); + AddDefaultHostConfigurationSources(hostConfigBuilder, args); + } + + internal static void SetDefaultContentRoot(IConfigurationBuilder hostConfigBuilder) { // If we're running anywhere other than C:\Windows\system32, we default to using the CWD for the ContentRoot. // However, since many things like Windows services and MSIX installers have C:\Windows\system32 as there CWD which is not likely @@ -216,7 +222,10 @@ internal static void ApplyDefaultHostConfiguration(IConfigurationBuilder hostCon new KeyValuePair(HostDefaults.ContentRootKey, cwd), }); } + } + internal static void AddDefaultHostConfigurationSources(IConfigurationBuilder hostConfigBuilder, string[]? args) + { hostConfigBuilder.AddEnvironmentVariables(prefix: "DOTNET_"); if (args is { Length: > 0 }) { diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj b/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj index 58cfcc97605652..d939261f252b56 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj +++ b/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj @@ -6,6 +6,8 @@ true true Hosting and startup infrastructures for applications. + true + 1 @@ -51,6 +53,7 @@ + diff --git a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostApplicationBuilderTests.cs b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostApplicationBuilderTests.cs index 458bf37eb7d005..d5ba003153769e 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostApplicationBuilderTests.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostApplicationBuilderTests.cs @@ -229,66 +229,128 @@ public void DisableDefaultIHostEnvironmentValues() Assert.IsAssignableFrom(env.ContentRootFileProvider); } - [Fact] - public void ConfigurationSettingCanInfluenceEnvironment() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void ConfigurationSettingCanInfluenceEnvironment(bool disableDefaults) { - using var config = new ConfigurationManager(); + var tempPath = CreateTempSubdirectory(); - config.AddInMemoryCollection(new KeyValuePair[] + try { - new(HostDefaults.ApplicationKey, "AppA" ), - new(HostDefaults.EnvironmentKey, "EnvA" ), - }); + using var config = new ConfigurationManager(); - var builder = new HostApplicationBuilder(new HostApplicationBuilderSettings - { - DisableDefaults = true, - Configuration = config, - }); + config.AddInMemoryCollection(new KeyValuePair[] + { + new(HostDefaults.ApplicationKey, "AppA" ), + new(HostDefaults.EnvironmentKey, "EnvA" ), + new(HostDefaults.ContentRootKey, tempPath) + }); + + var builder = new HostApplicationBuilder(new HostApplicationBuilderSettings + { + DisableDefaults = disableDefaults, + Configuration = config, + }); - Assert.Equal("AppA", builder.Configuration[HostDefaults.ApplicationKey]); - Assert.Equal("EnvA", builder.Configuration[HostDefaults.EnvironmentKey]); + Assert.Equal("AppA", builder.Configuration[HostDefaults.ApplicationKey]); + Assert.Equal("EnvA", builder.Configuration[HostDefaults.EnvironmentKey]); + Assert.Equal(tempPath, builder.Configuration[HostDefaults.ContentRootKey]); - Assert.Equal("AppA", builder.Environment.ApplicationName); - Assert.Equal("EnvA", builder.Environment.EnvironmentName); + Assert.Equal("AppA", builder.Environment.ApplicationName); + Assert.Equal("EnvA", builder.Environment.EnvironmentName); + Assert.Equal(tempPath, builder.Environment.ContentRootPath); + var fileProviderFromBuilder = Assert.IsType(builder.Environment.ContentRootFileProvider); + Assert.Equal(tempPath, fileProviderFromBuilder.Root); - using IHost host = builder.Build(); + using IHost host = builder.Build(); - var hostEnvironmentFromServices = host.Services.GetRequiredService(); - Assert.Equal("AppA", hostEnvironmentFromServices.ApplicationName); - Assert.Equal("EnvA", hostEnvironmentFromServices.EnvironmentName); + var hostEnvironmentFromServices = host.Services.GetRequiredService(); + Assert.Equal("AppA", hostEnvironmentFromServices.ApplicationName); + Assert.Equal("EnvA", hostEnvironmentFromServices.EnvironmentName); + Assert.Equal(tempPath, hostEnvironmentFromServices.ContentRootPath); + var fileProviderFromServices = Assert.IsType(hostEnvironmentFromServices.ContentRootFileProvider); + Assert.Equal(tempPath, fileProviderFromServices.Root); + } + finally + { + Directory.Delete(tempPath); + } } - [Fact] - public void DirectSettingsOverrideConfigurationSetting() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void DirectSettingsOverrideConfigurationSetting(bool disableDefaults) { - using var config = new ConfigurationManager(); + var tempPath = CreateTempSubdirectory(); - config.AddInMemoryCollection(new KeyValuePair[] + try { - new(HostDefaults.ApplicationKey, "AppA" ), - new(HostDefaults.EnvironmentKey, "EnvA" ), - }); + using var config = new ConfigurationManager(); - var builder = new HostApplicationBuilder(new HostApplicationBuilderSettings - { - DisableDefaults = true, - Configuration = config, - ApplicationName = "AppB", - EnvironmentName = "EnvB", - }); + config.AddInMemoryCollection(new KeyValuePair[] + { + new(HostDefaults.ApplicationKey, "AppA" ), + new(HostDefaults.EnvironmentKey, "EnvA" ), + }); - Assert.Equal("AppB", builder.Configuration[HostDefaults.ApplicationKey]); - Assert.Equal("EnvB", builder.Configuration[HostDefaults.EnvironmentKey]); + var builder = new HostApplicationBuilder(new HostApplicationBuilderSettings + { + DisableDefaults = disableDefaults, + Configuration = config, + ApplicationName = "AppB", + EnvironmentName = "EnvB", + ContentRootPath = tempPath, + }); - Assert.Equal("AppB", builder.Environment.ApplicationName); - Assert.Equal("EnvB", builder.Environment.EnvironmentName); + Assert.Equal("AppB", builder.Configuration[HostDefaults.ApplicationKey]); + Assert.Equal("EnvB", builder.Configuration[HostDefaults.EnvironmentKey]); + Assert.Equal(tempPath, builder.Configuration[HostDefaults.ContentRootKey]); - using IHost host = builder.Build(); + Assert.Equal("AppB", builder.Environment.ApplicationName); + Assert.Equal("EnvB", builder.Environment.EnvironmentName); + Assert.Equal(tempPath, builder.Environment.ContentRootPath); + var fileProviderFromBuilder = Assert.IsType(builder.Environment.ContentRootFileProvider); + Assert.Equal(tempPath, fileProviderFromBuilder.Root); - var hostEnvironmentFromServices = host.Services.GetRequiredService(); - Assert.Equal("AppB", hostEnvironmentFromServices.ApplicationName); - Assert.Equal("EnvB", hostEnvironmentFromServices.EnvironmentName); + using IHost host = builder.Build(); + + var hostEnvironmentFromServices = host.Services.GetRequiredService(); + Assert.Equal("AppB", hostEnvironmentFromServices.ApplicationName); + Assert.Equal("EnvB", hostEnvironmentFromServices.EnvironmentName); + Assert.Equal(tempPath, hostEnvironmentFromServices.ContentRootPath); + var fileProviderFromServices = Assert.IsType(hostEnvironmentFromServices.ContentRootFileProvider); + Assert.Equal(tempPath, fileProviderFromServices.Root); + } + finally + { + Directory.Delete(tempPath); + } + } + + private static string CreateTempSubdirectory() + { +#if NETCOREAPP + DirectoryInfo directoryInfo = Directory.CreateTempSubdirectory(); +#else + DirectoryInfo directoryInfo = new DirectoryInfo(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())); + directoryInfo.Create(); +#endif + + // PhysicalFileProvider will always ensure the path has a trailing slash + return EnsureTrailingSlash(directoryInfo.FullName); + } + + private static string EnsureTrailingSlash(string path) + { + if (!string.IsNullOrEmpty(path) && + path[path.Length - 1] != Path.DirectorySeparatorChar) + { + return path + Path.DirectorySeparatorChar; + } + + return path; } [Fact]