diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.FrameworkServices/Hosting/ConfigurationManagerConfigExtensions.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.FrameworkServices/Hosting/ConfigurationManagerConfigExtensions.cs index 1afc3a4eb..c78b87094 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.FrameworkServices/Hosting/ConfigurationManagerConfigExtensions.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.FrameworkServices/Hosting/ConfigurationManagerConfigExtensions.cs @@ -1,9 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.CodeDom.Compiler; using System.Collections; using System.Configuration; using System.Diagnostics; +using System.Reflection; +using System.Web; using System.Web.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.Memory; @@ -50,19 +53,19 @@ public static IConfigurationManager UseHostingEnvironmentFallback(this IConfigur if (configuration[HostDefaults.ApplicationKey] is null) { optionList = new(); - optionList.Add(new KeyValuePair(HostDefaults.ApplicationKey, HostingEnvironment.SiteName)); + optionList.Add(new(HostDefaults.ApplicationKey, GetDefaultApplicationName())); } if (configuration[HostDefaults.EnvironmentKey] is null && (HostingEnvironment.IsDevelopmentEnvironment || IsIISExpress())) { optionList ??= new(); - optionList.Add(new KeyValuePair(HostDefaults.EnvironmentKey, Environments.Development)); + optionList.Add(new(HostDefaults.EnvironmentKey, Environments.Development)); } if (configuration[HostDefaults.ContentRootKey] is null) { optionList ??= new(); - optionList.Add(new KeyValuePair(HostDefaults.ContentRootKey, HostingEnvironment.ApplicationPhysicalPath)); + optionList.Add(new(HostDefaults.ContentRootKey, HostingEnvironment.ApplicationPhysicalPath)); } if (optionList is not null) @@ -70,13 +73,45 @@ public static IConfigurationManager UseHostingEnvironmentFallback(this IConfigur configuration.AddInMemoryCollection(optionList); } + return configuration; + + /// + /// Does a best effort to get the name of the assembly that was launched by trying to find it via the active . + /// By using the name of the assembly, it matches ASP.NET Core behavior and allows things like UserSecrets to work which use that name to + /// identify where the secrets are. + /// + static string GetDefaultApplicationName() + { + // When called in HttpApplication.Application_Start, there is an initial HttpContext.Current that is set even though we're not in an actual request + if (HttpContext.Current is { ApplicationInstance: { } app }) + { + var type = app.GetType(); + + // The ApplicationInstance is generally a derived one generated by ASP.NET, so we need to check base types until we find the real one. + // The generated ones are marked with the GeneratedCodeAttribute, so we can check for that and skip them. + while (type.Assembly.GetCustomAttribute() is { } && type.BaseType.GetType() != typeof(HttpApplication)) + { + type = type.BaseType; + } + + var name = type.Assembly.GetName().Name; + + if (type != typeof(HttpApplication) && !string.IsNullOrEmpty(name)) + { + return name; + } + } + + // Fallback in case HttpContext.Current is not available; most likely this will be the case in the test environment where the HttpApplication + // is not starting up + return HostingEnvironment.SiteName ?? AppDomain.CurrentDomain.FriendlyName; + } + static bool IsIISExpress() { using var currentProcess = Process.GetCurrentProcess(); return string.Equals(currentProcess.ProcessName, "iisexpress", StringComparison.Ordinal); } - - return configuration; } }