Skip to content
This repository has been archived by the owner on Dec 19, 2018. It is now read-only.

Decouple ApplicationName from any assembly names #1180

Closed
wants to merge 10 commits into from
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Threading;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.Extensions.Configuration;
Expand Down Expand Up @@ -49,19 +50,67 @@ public static IWebHostBuilder CaptureStartupErrors(this IWebHostBuilder hostBuil
/// <param name="hostBuilder">The <see cref="IWebHostBuilder"/> to configure.</param>
/// <param name="startupAssemblyName">The name of the assembly containing the startup type.</param>
/// <returns>The <see cref="IWebHostBuilder"/>.</returns>
[Obsolete("This method is obsolete and will be removed in a future version. Use UseStartupAssembly instead.")]
public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, string startupAssemblyName)
{
return hostBuilder.UseStartupAssembly(startupAssemblyName);
}

/// <summary>
/// Specify the assembly containing the startup type to be used by the web host.
/// </summary>
/// <param name="hostBuilder">The <see cref="IWebHostBuilder"/> to configure.</param>
/// <param name="assembly">The assembly containing the startup type.</param>
/// <returns>The <see cref="IWebHostBuilder"/>.</returns>
public static IWebHostBuilder UseStartupAssembly(this IWebHostBuilder hostBuilder, Assembly assembly)
{
if (assembly == null)
{
throw new ArgumentNullException(nameof(assembly));
}

return hostBuilder.UseStartupAssembly(assembly.GetName().Name);
}

/// <summary>
/// Specify the assembly containing the startup type to be used by the web host.
/// </summary>
/// <param name="hostBuilder">The <see cref="IWebHostBuilder"/> to configure.</param>
/// <param name="startupAssemblyName">The name of the assembly containing the startup type.</param>
/// <returns>The <see cref="IWebHostBuilder"/>.</returns>
public static IWebHostBuilder UseStartupAssembly(this IWebHostBuilder hostBuilder, string startupAssemblyName)
{
if (startupAssemblyName == null)
{
throw new ArgumentNullException(nameof(startupAssemblyName));
}


return hostBuilder
.UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName)
.UseSetting(WebHostDefaults.FindStartupTypeKey, "true")
.UseSetting(WebHostDefaults.StartupAssemblyKey, startupAssemblyName);
}

/// <summary>
/// Specify the application name. This can be retrieved from <see cref="IHostingEnvironment.ApplicationName"/>.
/// </summary>
/// <param name="hostBuilder">The <see cref="IWebHostBuilder"/> to configure.</param>
/// <param name="applicationName">The name of the application.</param>
/// <returns>The <see cref="IWebHostBuilder"/>.</returns>
public static IWebHostBuilder UseApplicationName(this IWebHostBuilder hostBuilder, string applicationName)
{
if (applicationName == null)
{
throw new ArgumentNullException(nameof(applicationName));
}

if (applicationName.Length == 0)
{
throw new ArgumentException("Application name must not be empty.", nameof(applicationName));
}

return hostBuilder.UseSetting(WebHostDefaults.ApplicationKey, applicationName);
}

/// <summary>
/// Specify the server to be used by the web host.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public static class WebHostDefaults
{
public static readonly string ApplicationKey = "applicationName";
public static readonly string StartupAssemblyKey = "startupAssembly";
public static readonly string FindStartupTypeKey = "findStartupType";
public static readonly string HostingStartupAssembliesKey = "hostingStartupAssemblies";

public static readonly string DetailedErrorsKey = "detailedErrors";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ public static void Initialize(this IHostingEnvironment hostingEnvironment, strin
{
throw new ArgumentNullException(nameof(options));
}
if (string.IsNullOrEmpty(applicationName))
{
throw new ArgumentException("A valid non-empty application name must be provided.", nameof(applicationName));
}
if (string.IsNullOrEmpty(contentRootPath))
{
throw new ArgumentException("A valid non-empty content root must be provided.", nameof(contentRootPath));
Expand Down
26 changes: 23 additions & 3 deletions src/Microsoft.AspNetCore.Hosting/Internal/WebHostOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using Microsoft.Extensions.Configuration;

namespace Microsoft.AspNetCore.Hosting.Internal
Expand All @@ -19,16 +20,33 @@ public WebHostOptions(IConfiguration configuration)
throw new ArgumentNullException(nameof(configuration));
}

ApplicationName = configuration[WebHostDefaults.ApplicationKey];
StartupAssembly = configuration[WebHostDefaults.StartupAssemblyKey];
var startupAssembly = configuration[WebHostDefaults.StartupAssemblyKey];

if (string.IsNullOrEmpty(startupAssembly))
{
// Fall back to entry assembly name if startup assembly hasn't been set.
startupAssembly = Assembly.GetEntryAssembly()?.GetName().Name;
}

var applicationName = configuration[WebHostDefaults.ApplicationKey];

if (string.IsNullOrEmpty(applicationName))
{
// Fall back to name of Startup assembly if application name hasn't been overridden.
applicationName = startupAssembly;
}

ApplicationName = applicationName;
StartupAssembly = startupAssembly;
FindStartupType = WebHostUtilities.ParseBool(configuration, WebHostDefaults.FindStartupTypeKey);
DetailedErrors = WebHostUtilities.ParseBool(configuration, WebHostDefaults.DetailedErrorsKey);
CaptureStartupErrors = WebHostUtilities.ParseBool(configuration, WebHostDefaults.CaptureStartupErrorsKey);
Environment = configuration[WebHostDefaults.EnvironmentKey];
WebRoot = configuration[WebHostDefaults.WebRootKey];
ContentRootPath = configuration[WebHostDefaults.ContentRootKey];
PreventHostingStartup = WebHostUtilities.ParseBool(configuration, WebHostDefaults.PreventHostingStartupKey);
// Search the primary assembly and configured assemblies.
HostingStartupAssemblies = $"{ApplicationName};{configuration[WebHostDefaults.HostingStartupAssembliesKey]}"
HostingStartupAssemblies = $"{StartupAssembly};{configuration[WebHostDefaults.HostingStartupAssembliesKey]}"
.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries) ?? new string[0];

var timeout = configuration[WebHostDefaults.ShutdownTimeoutKey];
Expand All @@ -41,6 +59,8 @@ public WebHostOptions(IConfiguration configuration)

public string ApplicationName { get; set; }

public bool FindStartupType { get; set; }

public bool PreventHostingStartup { get; set; }

public IReadOnlyList<string> HostingStartupAssemblies { get; set; }
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.AspNetCore.Hosting/WebHostBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ private IServiceCollection BuildCommonServices(out AggregateException hostingSta
// Ensure object pooling is available everywhere.
services.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();

if (!string.IsNullOrEmpty(_options.StartupAssembly))
if (_options.FindStartupType)
{
try
{
Expand Down
4 changes: 2 additions & 2 deletions src/Microsoft.AspNetCore.Hosting/WebHostBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public static IWebHostBuilder Configure(this IWebHostBuilder hostBuilder, Action
var startupAssemblyName = configureApp.GetMethodInfo().DeclaringType.GetTypeInfo().Assembly.GetName().Name;

return hostBuilder
.UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName)
.UseSetting(WebHostDefaults.StartupAssemblyKey, startupAssemblyName)
.ConfigureServices(services =>
{
services.AddSingleton<IStartup>(sp =>
Expand All @@ -52,7 +52,7 @@ public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type
var startupAssemblyName = startupType.GetTypeInfo().Assembly.GetName().Name;

return hostBuilder
.UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName)
.UseSetting(WebHostDefaults.StartupAssemblyKey, startupAssemblyName)
.ConfigureServices(services =>
{
if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo()))
Expand Down
2 changes: 1 addition & 1 deletion test/Microsoft.AspNetCore.Hosting.TestSites/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public static void Main(string[] args)
factory.AddConsole();
factory.AddFilter<ConsoleLoggerProvider>(level => level >= LogLevel.Warning);
})
.UseStartup("Microsoft.AspNetCore.Hosting.TestSites");
.UseStartupAssembly("Microsoft.AspNetCore.Hosting.TestSites");

if (config["STARTMECHANIC"] == "Run")
{
Expand Down
49 changes: 37 additions & 12 deletions test/Microsoft.AspNetCore.Hosting.Tests/WebHostBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public void Build_honors_UseStartup_with_string()
{
var builder = CreateWebHostBuilder().UseServer(new TestServer());

using (var host = (WebHost)builder.UseStartup("MyStartupAssembly").Build())
using (var host = (WebHost)builder.UseStartupAssembly("MyStartupAssembly").Build())
{
Assert.Equal("MyStartupAssembly", host.Options.ApplicationName);
Assert.Equal("MyStartupAssembly", host.Options.StartupAssembly);
Expand All @@ -46,7 +46,7 @@ public async Task StartupMissing_Fallback()
{
var builder = CreateWebHostBuilder();
var server = new TestServer();
using (var host = builder.UseServer(server).UseStartup("MissingStartupAssembly").Build())
using (var host = builder.UseServer(server).UseStartupAssembly("MissingStartupAssembly").Build())
{
await host.StartAsync();
await AssertResponseContains(server.RequestDelegate, "MissingStartupAssembly");
Expand Down Expand Up @@ -492,7 +492,7 @@ public void UseEnvironmentIsNotOverriden()
.UseConfiguration(config)
.UseEnvironment(expected)
.UseServer(new TestServer())
.UseStartup("Microsoft.AspNetCore.Hosting.Tests")
.UseStartupAssembly("Microsoft.AspNetCore.Hosting.Tests")
.Build())
{
Assert.Equal(expected, host.Services.GetService<IHostingEnvironment>().EnvironmentName);
Expand All @@ -515,7 +515,7 @@ public void BuildAndDispose()
.UseConfiguration(config)
.UseEnvironment(expected)
.UseServer(new TestServer())
.UseStartup("Microsoft.AspNetCore.Hosting.Tests")
.UseStartupAssembly("Microsoft.AspNetCore.Hosting.Tests")
.Build()) { }
}

Expand All @@ -534,7 +534,7 @@ public void UseBasePathConfiguresBasePath()
.UseConfiguration(config)
.UseContentRoot("/")
.UseServer(new TestServer())
.UseStartup("Microsoft.AspNetCore.Hosting.Tests")
.UseStartupAssembly("Microsoft.AspNetCore.Hosting.Tests")
.Build())
{
Assert.Equal("/", host.Services.GetService<IHostingEnvironment>().ContentRootPath);
Expand All @@ -547,7 +547,7 @@ public void RelativeContentRootIsResolved()
using (var host = new WebHostBuilder()
.UseContentRoot("testroot")
.UseServer(new TestServer())
.UseStartup("Microsoft.AspNetCore.Hosting.Tests")
.UseStartupAssembly("Microsoft.AspNetCore.Hosting.Tests")
.Build())
{
var basePath = host.Services.GetRequiredService<IHostingEnvironment>().ContentRootPath;
Expand All @@ -561,7 +561,7 @@ public void DefaultContentRootIsApplicationBasePath()
{
using (var host = new WebHostBuilder()
.UseServer(new TestServer())
.UseStartup("Microsoft.AspNetCore.Hosting.Tests")
.UseStartupAssembly("Microsoft.AspNetCore.Hosting.Tests")
.Build())
{
var appBase = AppContext.BaseDirectory;
Expand All @@ -576,11 +576,9 @@ public void DefaultApplicationNameWithNoStartupThrows()
var host = new WebHostBuilder()
.UseServer(new TestServer());

var ex = Assert.Throws<ArgumentException>(() => host.Build());
var ex = Assert.Throws<InvalidOperationException>(() => host.Build());

// ArgumentException adds "Parameter name" to the message and this is the cleanest way to make sure we get the right
// expected string
Assert.Equal(new ArgumentException("A valid non-empty application name must be provided.", "applicationName").Message , ex.Message);
Assert.Equal("No service for type 'Microsoft.AspNetCore.Hosting.IStartup' has been registered.", ex.Message);
}

[Fact]
Expand All @@ -589,7 +587,7 @@ public void DefaultApplicationNameWithUseStartupOfString()
var builder = new ConfigurationBuilder();
using (var host = new WebHostBuilder()
.UseServer(new TestServer())
.UseStartup(typeof(Startup).Assembly.GetName().Name)
.UseStartupAssembly(typeof(Startup).Assembly.GetName().Name)
.Build())
{
var hostingEnv = host.Services.GetService<IHostingEnvironment>();
Expand All @@ -611,6 +609,33 @@ public void DefaultApplicationNameWithUseStartupOfT()
}
}

[Fact]
public void CustomApplicationName()
{
using (var host = new WebHostBuilder()
.UseServer(new TestServer())
.UseApplicationName("Test Application")
.UseStartup<StartupNoServices>()
.Build())
{
var hostingEnv = host.Services.GetService<IHostingEnvironment>();
Assert.Equal("Test Application", hostingEnv.ApplicationName);
}
}

[Fact]
public void DefaultApplicationNameWithInjectedIStartup()
{
using (var host = new WebHostBuilder()
.UseServer(new TestServer())
.ConfigureServices(s => s.AddSingleton<IStartup, StartupNoServices>())
.Build())
{
var hostingEnv = host.Services.GetService<IHostingEnvironment>();
Assert.Equal(Assembly.GetEntryAssembly()?.GetName().Name, hostingEnv.ApplicationName);
}
}

[Fact]
public void DefaultApplicationNameWithUseStartupOfType()
{
Expand Down
Loading