Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -50,33 +53,65 @@ public static IConfigurationManager UseHostingEnvironmentFallback(this IConfigur
if (configuration[HostDefaults.ApplicationKey] is null)
{
optionList = new();
optionList.Add(new KeyValuePair<string, string?>(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<string, string?>(HostDefaults.EnvironmentKey, Environments.Development));
optionList.Add(new(HostDefaults.EnvironmentKey, Environments.Development));
}

if (configuration[HostDefaults.ContentRootKey] is null)
{
optionList ??= new();
optionList.Add(new KeyValuePair<string, string?>(HostDefaults.ContentRootKey, HostingEnvironment.ApplicationPhysicalPath));
optionList.Add(new(HostDefaults.ContentRootKey, HostingEnvironment.ApplicationPhysicalPath));
}

if (optionList is not null)
{
configuration.AddInMemoryCollection(optionList);
}

return configuration;

/// <summary>
/// Does a best effort to get the name of the assembly that was launched by trying to find it via the active <see cref="HttpApplication"/>.
/// 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.
/// </summary>
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<GeneratedCodeAttribute>() 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;
}
}