This change introduces a new service `IStartup` that can be registered in the hosting container to override any startup logic. `UseStartup` overloads have been changed to detect `IStartup` and directly put it in the container, or to wrapping it with a `ConventionBasedStartup` implementation to preserve the existing behavior.

- Remove IStartupLoader and add `IStartup` instead that matches the signature hosting cares about
- Moved `UseStartup` to extension methods
- Move existing logic into `ConventionBasedStartup` class
This commit is contained in:
David Fowler 2016-04-17 00:20:34 -07:00
parent dd6bf6a205
commit 8f5f8d28d0
30 changed files with 280 additions and 265 deletions

View File

@ -1,3 +1,3 @@
{
"projects": ["src"]
}
}

View File

@ -8,16 +8,17 @@ using Microsoft.Extensions.DependencyInjection;
namespace SampleStartups
{
public class StartupBlockingOnStart
public class StartupBlockingOnStart : StartupBase
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
public override IServiceProvider ConfigureServices(IServiceCollection services)
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
return base.ConfigureServices(services);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app)
public override void Configure(IApplicationBuilder app)
{
app.Run(async (context) =>
{

View File

@ -1,22 +1,15 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
// Note that this sample will not run. It is only here to illustrate usage patterns.
namespace SampleStartups
{
public class StartupConfigureAddresses
public class StartupConfigureAddresses : StartupBase
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app)
public override void Configure(IApplicationBuilder app)
{
app.Run(async (context) =>
{

View File

@ -3,25 +3,18 @@ using System.Collections.Generic;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
// Note that this sample will not run. It is only here to illustrate usage patterns.
namespace SampleStartups
{
public class StartupExternallyControlled
public class StartupExternallyControlled : StartupBase
{
private IWebHost _host;
private readonly List<string> _urls = new List<string>();
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app)
public override void Configure(IApplicationBuilder app)
{
app.Run(async (context) =>
{

View File

@ -1,22 +1,15 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
// Note that this sample will not run. It is only here to illustrate usage patterns.
namespace SampleStartups
{
public class StartupHelloWorld
public class StartupHelloWorld : StartupBase
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app)
public override void Configure(IApplicationBuilder app)
{
app.Run(async (context) =>
{

View File

@ -0,0 +1,16 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Hosting
{
public interface IStartup
{
IServiceProvider ConfigureServices(IServiceCollection services);
void Configure(IApplicationBuilder app);
}
}

View File

@ -2,8 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@ -26,13 +24,6 @@ namespace Microsoft.AspNetCore.Hosting
/// <returns>The <see cref="IWebHostBuilder"/>.</returns>
IWebHostBuilder UseLoggerFactory(ILoggerFactory loggerFactory);
/// <summary>
/// Specify the startup type to be used by the web host.
/// </summary>
/// <param name="startupType">The <see cref="Type"/> to be used.</param>
/// <returns>The <see cref="IWebHostBuilder"/>.</returns>
IWebHostBuilder UseStartup(Type startupType);
/// <summary>
/// Specify the delegate that is used to configure the services of the web application.
/// </summary>
@ -40,13 +31,6 @@ namespace Microsoft.AspNetCore.Hosting
/// <returns>The <see cref="IWebHostBuilder"/>.</returns>
IWebHostBuilder ConfigureServices(Action<IServiceCollection> configureServices);
/// <summary>
/// Specify the startup method to be used to configure the web application.
/// </summary>
/// <param name="configureApplication">The delegate that configures the <see cref="IApplicationBuilder"/>.</param>
/// <returns>The <see cref="IWebHostBuilder"/>.</returns>
IWebHostBuilder Configure(Action<IApplicationBuilder> configureApplication);
/// <summary>
/// Adds a delegate for configuring the provided <see cref="ILoggerFactory"/>. This may be called multiple times.
/// </summary>

View File

@ -5,7 +5,9 @@ namespace Microsoft.AspNetCore.Hosting
{
public static class WebHostDefaults
{
public static readonly string ApplicationKey = "application";
public static readonly string ApplicationKey = "applicationName";
public static readonly string StartupAssemblyKey = "startupAssembly";
public static readonly string DetailedErrorsKey = "detailedErrors";
public static readonly string EnvironmentKey = "environment";
public static readonly string ServerKey = "server";

View File

@ -3,7 +3,6 @@
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Startup;
namespace Microsoft.AspNetCore.Hosting.Internal
{

View File

@ -6,7 +6,7 @@ using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Hosting.Startup
namespace Microsoft.AspNetCore.Hosting.Internal
{
public class ConfigureBuilder
{
@ -51,4 +51,4 @@ namespace Microsoft.AspNetCore.Hosting.Startup
MethodInfo.Invoke(instance, parameters);
}
}
}
}

View File

@ -6,7 +6,7 @@ using System.Linq;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Hosting.Startup
namespace Microsoft.AspNetCore.Hosting.Internal
{
public class ConfigureServicesBuilder
{

View File

@ -1,4 +1,7 @@
using System;
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Hosting.Server;

View File

@ -2,45 +2,31 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Hosting.Startup
namespace Microsoft.AspNetCore.Hosting.Internal
{
public class StartupLoader : IStartupLoader
public class StartupLoader
{
private readonly IServiceProvider _services;
private readonly IHostingEnvironment _hostingEnv;
public StartupLoader(IServiceProvider services, IHostingEnvironment hostingEnv)
public static StartupMethods LoadMethods(IServiceProvider services, Type startupType, string environmentName)
{
_services = services;
_hostingEnv = hostingEnv;
}
public StartupMethods LoadMethods(
Type startupType,
IList<string> diagnosticMessages)
{
var environmentName = _hostingEnv.EnvironmentName;
var configureMethod = FindConfigureDelegate(startupType, environmentName);
var servicesMethod = FindConfigureServicesDelegate(startupType, environmentName);
object instance = null;
if (!configureMethod.MethodInfo.IsStatic || (servicesMethod != null && !servicesMethod.MethodInfo.IsStatic))
{
instance = ActivatorUtilities.GetServiceOrCreateInstance(_services, startupType);
instance = ActivatorUtilities.GetServiceOrCreateInstance(services, startupType);
}
return new StartupMethods(configureMethod.Build(instance), servicesMethod?.Build(instance));
}
public Type FindStartupType(string startupAssemblyName, IList<string> diagnosticMessages)
public static Type FindStartupType(string startupAssemblyName, string environmentName)
{
var environmentName = _hostingEnv.EnvironmentName;
if (string.IsNullOrEmpty(startupAssemblyName))
{
throw new ArgumentException(

View File

@ -5,7 +5,7 @@ using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Hosting.Startup
namespace Microsoft.AspNetCore.Hosting.Internal
{
public class StartupMethods
{

View File

@ -6,10 +6,10 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Builder;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Hosting.Startup;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Configuration;
@ -22,7 +22,9 @@ namespace Microsoft.AspNetCore.Hosting.Internal
public class WebHost : IWebHost
{
private readonly IServiceCollection _applicationServiceCollection;
private readonly IStartupLoader _startupLoader;
private IStartup _startup;
private readonly IServiceProvider _hostingServiceProvider;
private readonly ApplicationLifetime _applicationLifetime;
private readonly WebHostOptions _options;
private readonly IConfiguration _config;
@ -34,16 +36,11 @@ namespace Microsoft.AspNetCore.Hosting.Internal
// Used for testing only
internal WebHostOptions Options => _options;
// Only one of these should be set
internal string StartupAssemblyName { get; set; }
internal StartupMethods Startup { get; set; }
internal Type StartupType { get; set; }
private IServer Server { get; set; }
public WebHost(
IServiceCollection appServices,
IStartupLoader startupLoader,
IServiceProvider hostingServiceProvider,
WebHostOptions options,
IConfiguration config)
{
@ -52,9 +49,9 @@ namespace Microsoft.AspNetCore.Hosting.Internal
throw new ArgumentNullException(nameof(appServices));
}
if (startupLoader == null)
if (hostingServiceProvider == null)
{
throw new ArgumentNullException(nameof(startupLoader));
throw new ArgumentNullException(nameof(hostingServiceProvider));
}
if (config == null)
@ -65,7 +62,7 @@ namespace Microsoft.AspNetCore.Hosting.Internal
_config = config;
_options = options;
_applicationServiceCollection = appServices;
_startupLoader = startupLoader;
_hostingServiceProvider = hostingServiceProvider;
_applicationLifetime = new ApplicationLifetime();
_applicationServiceCollection.AddSingleton<IApplicationLifetime>(_applicationLifetime);
}
@ -116,37 +113,18 @@ namespace Microsoft.AspNetCore.Hosting.Internal
if (_applicationServices == null)
{
EnsureStartup();
_applicationServices = Startup.ConfigureServicesDelegate(_applicationServiceCollection);
_applicationServices = _startup.ConfigureServices(_applicationServiceCollection);
}
}
private void EnsureStartup()
{
if (Startup != null)
if (_startup != null)
{
return;
}
if (StartupType == null)
{
var diagnosticTypeMessages = new List<string>();
StartupType = _startupLoader.FindStartupType(StartupAssemblyName, diagnosticTypeMessages);
if (StartupType == null)
{
throw new ArgumentException(
diagnosticTypeMessages.Aggregate("Failed to find a startup type for the web application.", (a, b) => a + "\r\n" + b),
StartupAssemblyName);
}
}
var diagnosticMessages = new List<string>();
Startup = _startupLoader.LoadMethods(StartupType, diagnosticMessages);
if (Startup == null)
{
throw new ArgumentException(
diagnosticMessages.Aggregate("Failed to find a startup entry point for the web application.", (a, b) => a + "\r\n" + b),
StartupAssemblyName);
}
_startup = _hostingServiceProvider.GetRequiredService<IStartup>();
}
private RequestDelegate BuildApplication()
@ -161,7 +139,7 @@ namespace Microsoft.AspNetCore.Hosting.Internal
builder.ApplicationServices = _applicationServices;
var startupFilters = _applicationServices.GetService<IEnumerable<IStartupFilter>>();
var configure = Startup.ConfigureDelegate;
Action<IApplicationBuilder> configure = _startup.Configure;
foreach (var filter in startupFilters.Reverse())
{
configure = filter.Configure(configure);

View File

@ -19,7 +19,8 @@ namespace Microsoft.AspNetCore.Hosting.Internal
throw new ArgumentNullException(nameof(configuration));
}
Application = configuration[WebHostDefaults.ApplicationKey];
ApplicationName = configuration[WebHostDefaults.ApplicationKey];
StartupAssembly = configuration[WebHostDefaults.StartupAssemblyKey];
DetailedErrors = ParseBool(configuration, WebHostDefaults.DetailedErrorsKey);
CaptureStartupErrors = ParseBool(configuration, WebHostDefaults.CaptureStartupErrorsKey);
Environment = configuration[WebHostDefaults.EnvironmentKey];
@ -28,7 +29,7 @@ namespace Microsoft.AspNetCore.Hosting.Internal
ContentRootPath = configuration[WebHostDefaults.ContentRootKey];
}
public string Application { get; set; }
public string ApplicationName { get; set; }
public bool DetailedErrors { get; set; }
@ -37,6 +38,8 @@ namespace Microsoft.AspNetCore.Hosting.Internal
public string Environment { get; set; }
public string ServerAssembly { get; set; }
public string StartupAssembly { get; set; }
public string WebRoot { get; set; }

View File

@ -0,0 +1,31 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Internal;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNet.Hosting
{
public class ConventionBasedStartup : IStartup
{
private readonly StartupMethods _methods;
public ConventionBasedStartup(StartupMethods methods)
{
_methods = methods;
}
public void Configure(IApplicationBuilder app)
{
_methods.ConfigureDelegate(app);
}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
return _methods.ConfigureServicesDelegate(services);
}
}
}

View File

@ -0,0 +1,21 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
namespace Microsoft.AspNet.Hosting
{
public class DelegateStartup : StartupBase
{
private Action<IApplicationBuilder> _configureApp;
public DelegateStartup(Action<IApplicationBuilder> configureApp)
{
_configureApp = configureApp;
}
public override void Configure(IApplicationBuilder app) => _configureApp(app);
}
}

View File

@ -1,15 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Hosting.Startup
{
public interface IStartupLoader
{
Type FindStartupType(string startupAssemblyName, IList<string> diagnosticMessages);
StartupMethods LoadMethods(Type startupType, IList<string> diagnosticMessages);
}
}

View File

@ -0,0 +1,19 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Hosting
{
public abstract class StartupBase : IStartup
{
public abstract void Configure(IApplicationBuilder app);
public virtual IServiceProvider ConfigureServices(IServiceCollection services)
{
return services.BuildServiceProvider();
}
}
}

View File

@ -15,7 +15,7 @@ using System.Text.Encodings.Web;
using Microsoft.Extensions.Internal;
using Microsoft.Extensions.PlatformAbstractions;
namespace Microsoft.AspNetCore.Hosting.Startup
namespace Microsoft.AspNetCore.Hosting
{
internal static class StartupExceptionPage
{

View File

@ -5,13 +5,12 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using System.Runtime.ExceptionServices;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNetCore.Hosting.Builder;
using Microsoft.AspNetCore.Hosting.Internal;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Startup;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@ -34,10 +33,6 @@ namespace Microsoft.AspNetCore.Hosting
private ILoggerFactory _loggerFactory;
private WebHostOptions _options;
// Only one of these should be set
private StartupMethods _startup;
private Type _startupType;
/// <summary>
/// Initializes a new instance of the <see cref="WebHostBuilder"/> class.
/// </summary>
@ -86,22 +81,6 @@ namespace Microsoft.AspNetCore.Hosting
return this;
}
/// <summary>
/// Specify the startup type to be used by the web host.
/// </summary>
/// <param name="startupType">The <see cref="Type"/> to be used.</param>
/// <returns>The <see cref="IWebHostBuilder"/>.</returns>
public IWebHostBuilder UseStartup(Type startupType)
{
if (startupType == null)
{
throw new ArgumentNullException(nameof(startupType));
}
_startupType = startupType;
return this;
}
/// <summary>
/// Adds a delegate for configuring additional services for the host or web application. This may be called
/// multiple times.
@ -119,22 +98,6 @@ namespace Microsoft.AspNetCore.Hosting
return this;
}
/// <summary>
/// Specify the startup method to be used to configure the web application.
/// </summary>
/// <param name="configureApp">The delegate that configures the <see cref="IApplicationBuilder"/>.</param>
/// <returns>The <see cref="IWebHostBuilder"/>.</returns>
public IWebHostBuilder Configure(Action<IApplicationBuilder> configureApp)
{
if (configureApp == null)
{
throw new ArgumentNullException(nameof(configureApp));
}
_startup = new StartupMethods(configureApp);
return this;
}
/// <summary>
/// Adds a delegate for configuring the provided <see cref="ILoggerFactory"/>. This may be called multiple times.
/// </summary>
@ -159,14 +122,7 @@ namespace Microsoft.AspNetCore.Hosting
var hostingServices = BuildHostingServices();
var hostingContainer = hostingServices.BuildServiceProvider();
var startupLoader = hostingContainer.GetRequiredService<IStartupLoader>();
var host = new WebHost(hostingServices, startupLoader, _options, _config);
// Only one of these should be set, but they are used in priority
host.Startup = _startup;
host.StartupType = _startupType;
host.StartupAssemblyName = _options.Application;
var host = new WebHost(hostingServices, hostingContainer, _options, _config);
host.Initialize();
@ -180,7 +136,7 @@ namespace Microsoft.AspNetCore.Hosting
var defaultPlatformServices = PlatformServices.Default;
var appEnvironment = defaultPlatformServices.Application;
var contentRootPath = ResolveContentRootPath(_options.ContentRootPath, appEnvironment.ApplicationBasePath);
var applicationName = ResolveApplicationName() ?? appEnvironment.ApplicationName;
var applicationName = _options.ApplicationName ?? appEnvironment.ApplicationName;
// Initialize the hosting environment
_hostingEnvironment.Initialize(applicationName, contentRootPath, _options);
@ -204,7 +160,6 @@ namespace Microsoft.AspNetCore.Hosting
//This is required to add ILogger of T.
services.AddLogging();
services.AddTransient<IStartupLoader, StartupLoader>();
services.AddTransient<IApplicationBuilderFactory, ApplicationBuilderFactory>();
services.AddTransient<IHttpContextFactory, HttpContextFactory>();
services.AddOptions();
@ -225,8 +180,51 @@ namespace Microsoft.AspNetCore.Hosting
if (!string.IsNullOrEmpty(_options.ServerAssembly))
{
// Add the server
var serverType = ServerLoader.ResolveServerType(_options.ServerAssembly);
services.AddSingleton(typeof(IServer), serverType);
try
{
var serverType = ServerLoader.ResolveServerType(_options.ServerAssembly);
services.AddSingleton(typeof(IServer), serverType);
}
catch (Exception ex)
{
var capture = ExceptionDispatchInfo.Capture(ex);
services.AddSingleton<IServer>(_ =>
{
capture.Throw();
return null;
});
}
}
if (!string.IsNullOrEmpty(_options.StartupAssembly))
{
try
{
var startupType = StartupLoader.FindStartupType(_options.StartupAssembly, _hostingEnvironment.EnvironmentName);
if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo()))
{
services.AddSingleton(typeof(IStartup), startupType);
}
else
{
services.AddSingleton(typeof(IStartup), sp =>
{
var hostingEnvironment = sp.GetRequiredService<IHostingEnvironment>();
var methods = StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName);
return new ConventionBasedStartup(methods);
});
}
}
catch (Exception ex)
{
var capture = ExceptionDispatchInfo.Capture(ex);
services.AddSingleton<IStartup>(_ =>
{
capture.Throw();
return null;
});
}
}
foreach (var configureServices in _configureServicesDelegates)
@ -249,22 +247,5 @@ namespace Microsoft.AspNetCore.Hosting
}
return Path.Combine(Path.GetFullPath(basePath), contentRootPath);
}
private string ResolveApplicationName()
{
if (_startup != null)
{
return _startup.ConfigureDelegate.Target.GetType().GetTypeInfo().Assembly.GetName().Name;
}
if (_startupType != null)
{
return _startupType.GetTypeInfo().Assembly.GetName().Name;
}
if (!string.IsNullOrEmpty(_options.Application))
{
return _options.Application;
}
return null;
}
}
}

View File

@ -4,6 +4,8 @@
using System;
using System.Linq;
using System.Reflection;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Internal;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.Extensions.Configuration;
@ -63,7 +65,58 @@ namespace Microsoft.AspNetCore.Hosting
{
return hostBuilder.UseSetting(WebHostDefaults.CaptureStartupErrorsKey, captureStartupErrors ? "true" : "false");
}
/// <summary>
/// Specify the startup method to be used to configure the web application.
/// </summary>
/// <param name="hostBuilder">The <see cref="IWebHostBuilder"/> to configure.</param>
/// <param name="configureApp">The delegate that configures the <see cref="IApplicationBuilder"/>.</param>
/// <returns>The <see cref="IWebHostBuilder"/>.</returns>
public static IWebHostBuilder Configure(this IWebHostBuilder hostBuilder, Action<IApplicationBuilder> configureApp)
{
if (configureApp == null)
{
throw new ArgumentNullException(nameof(configureApp));
}
var startupAssemblyName = configureApp.Target.GetType().GetTypeInfo().Assembly.GetName().Name;
return hostBuilder.UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName)
.ConfigureServices(services =>
{
services.AddSingleton<IStartup>(new DelegateStartup(configureApp));
});
}
/// <summary>
/// Specify the startup type to be used by the web host.
/// </summary>
/// <param name="hostBuilder">The <see cref="IWebHostBuilder"/> to configure.</param>
/// <param name="startupType">The <see cref="Type"/> to be used.</param>
/// <returns>The <see cref="IWebHostBuilder"/>.</returns>
public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type startupType)
{
var startupAssemblyName = startupType.GetTypeInfo().Assembly.GetName().Name;
return hostBuilder.UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName)
.ConfigureServices(services =>
{
if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo()))
{
services.AddSingleton(typeof(IStartup), startupType);
}
else
{
services.AddSingleton(typeof(IStartup), sp =>
{
var hostingEnvironment = sp.GetRequiredService<IHostingEnvironment>();
return new ConventionBasedStartup(StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName));
});
}
});
}
/// <summary>
/// Specify the startup type to be used by the web host.
/// </summary>
@ -88,7 +141,10 @@ namespace Microsoft.AspNetCore.Hosting
throw new ArgumentNullException(nameof(startupAssemblyName));
}
return hostBuilder.UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName);
return hostBuilder
.UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName)
.UseSetting(WebHostDefaults.StartupAssemblyKey, startupAssemblyName);
}
/// <summary>

View File

@ -5,10 +5,11 @@ using System;
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Builder.Internal;
using Microsoft.AspNetCore.Hosting.Internal;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace Microsoft.AspNetCore.Hosting.Startup.Tests
namespace Microsoft.AspNetCore.Hosting.Tests
{
public class ConfigureBuilderTests
{
@ -51,4 +52,4 @@ namespace Microsoft.AspNetCore.Hosting.Startup.Tests
}
}
}
}
}

View File

@ -5,13 +5,13 @@ using Microsoft.AspNetCore.Builder;
namespace Microsoft.AspNetCore.Hosting.Fakes
{
public class StartupNoServices
public class StartupNoServices : Hosting.StartupBase
{
public StartupNoServices()
{
}
public void Configure(IApplicationBuilder builder)
public override void Configure(IApplicationBuilder builder)
{
}
}

View File

@ -8,7 +8,6 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Builder.Internal;
using Microsoft.AspNetCore.Hosting.Fakes;
using Microsoft.AspNetCore.Hosting.Internal;
using Microsoft.AspNetCore.Hosting.Startup;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Xunit;
@ -26,11 +25,8 @@ namespace Microsoft.AspNetCore.Hosting.Tests
serviceCollection.AddSingleton<IFakeStartupCallback>(this);
var services = serviceCollection.BuildServiceProvider();
var diagnosticMessages = new List<string>();
var hostingEnv = new HostingEnvironment { EnvironmentName = "WithServices" };
var loader = new StartupLoader(services, hostingEnv);
var type = loader.FindStartupType("Microsoft.AspNetCore.Hosting.Tests", diagnosticMessages);
var startup = loader.LoadMethods(type, diagnosticMessages);
var type = StartupLoader.FindStartupType("Microsoft.AspNetCore.Hosting.Tests", "WithServices");
var startup = StartupLoader.LoadMethods(services, type, "WithServices");
var app = new ApplicationBuilder(services);
app.ApplicationServices = startup.ConfigureServicesDelegate(serviceCollection);
@ -52,11 +48,8 @@ namespace Microsoft.AspNetCore.Hosting.Tests
{
var services = new ServiceCollection().BuildServiceProvider();
var diagnosticMessages = new List<string>();
var hostingEnv = new HostingEnvironment { EnvironmentName = environment };
var loader = new StartupLoader(services, hostingEnv);
var type = loader.FindStartupType("Microsoft.AspNetCore.Hosting.Tests", diagnosticMessages);
var startup = loader.LoadMethods(type, diagnosticMessages);
var type = StartupLoader.FindStartupType("Microsoft.AspNetCore.Hosting.Tests", environment);
var startup = StartupLoader.LoadMethods(services, type, environment);
var app = new ApplicationBuilder(services);
app.ApplicationServices = startup.ConfigureServicesDelegate(new ServiceCollection());
@ -74,13 +67,8 @@ namespace Microsoft.AspNetCore.Hosting.Tests
var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<IFakeStartupCallback>(this);
var services = serviceCollection.BuildServiceProvider();
var diagnosticMessages = new List<string>();
var hostingEnv = new HostingEnvironment { EnvironmentName = "Boom" };
var loader = new StartupLoader(services, hostingEnv);
var type = loader.FindStartupType("Microsoft.AspNetCore.Hosting.Tests", diagnosticMessages);
var ex = Assert.Throws<InvalidOperationException>(() => loader.LoadMethods(type, diagnosticMessages));
var type = StartupLoader.FindStartupType("Microsoft.AspNetCore.Hosting.Tests", "Boom");
var ex = Assert.Throws<InvalidOperationException>(() => StartupLoader.LoadMethods(services, type, "Boom"));
Assert.Equal("A public method named 'ConfigureBoom' or 'Configure' could not be found in the 'Microsoft.AspNetCore.Hosting.Fakes.StartupBoom' type.", ex.Message);
}
@ -91,12 +79,9 @@ namespace Microsoft.AspNetCore.Hosting.Tests
serviceCollection.AddSingleton<IFakeStartupCallback>(this);
var services = serviceCollection.BuildServiceProvider();
var diagnosticMessages = new List<string>();
var hostingEnv = new HostingEnvironment { EnvironmentName = "TwoConfigures" };
var loader = new StartupLoader(services, hostingEnv);
var type = loader.FindStartupType("Microsoft.AspNetCore.Hosting.Tests", diagnosticMessages);
var type = StartupLoader.FindStartupType("Microsoft.AspNetCore.Hosting.Tests", "TwoConfigures");
var ex = Assert.Throws<InvalidOperationException>(() => loader.LoadMethods(type, diagnosticMessages));
var ex = Assert.Throws<InvalidOperationException>(() => StartupLoader.LoadMethods(services, type, "TwoConfigures"));
Assert.Equal("Having multiple overloads of method 'Configure' is not supported.", ex.Message);
}
@ -108,11 +93,9 @@ namespace Microsoft.AspNetCore.Hosting.Tests
var services = serviceCollection.BuildServiceProvider();
var diagnosticMessages = new List<string>();
var hostingEnv = new HostingEnvironment { EnvironmentName = "PrivateConfigure" };
var loader = new StartupLoader(services, hostingEnv);
var type = loader.FindStartupType("Microsoft.AspNetCore.Hosting.Tests", diagnosticMessages);
var type = StartupLoader.FindStartupType("Microsoft.AspNetCore.Hosting.Tests", "PrivateConfigure");
var ex = Assert.Throws<InvalidOperationException>(() => loader.LoadMethods(type, diagnosticMessages));
var ex = Assert.Throws<InvalidOperationException>(() => StartupLoader.LoadMethods(services, type, "PrivateConfigure"));
Assert.Equal("A public method named 'ConfigurePrivateConfigure' or 'Configure' could not be found in the 'Microsoft.AspNetCore.Hosting.Fakes.StartupPrivateConfigure' type.", ex.Message);
}
@ -123,12 +106,9 @@ namespace Microsoft.AspNetCore.Hosting.Tests
serviceCollection.AddSingleton<IFakeStartupCallback>(this);
var services = serviceCollection.BuildServiceProvider();
var diagnosticMessages = new List<string>();
var hostingEnv = new HostingEnvironment { EnvironmentName = "TwoConfigureServices" };
var loader = new StartupLoader(services, hostingEnv);
var type = loader.FindStartupType("Microsoft.AspNetCore.Hosting.Tests", diagnosticMessages);
var type = StartupLoader.FindStartupType("Microsoft.AspNetCore.Hosting.Tests", "TwoConfigureServices");
var ex = Assert.Throws<InvalidOperationException>(() => loader.LoadMethods(type, diagnosticMessages));
var ex = Assert.Throws<InvalidOperationException>(() => StartupLoader.LoadMethods(services, type, "TwoConfigureServices"));
Assert.Equal("Having multiple overloads of method 'ConfigureServices' is not supported.", ex.Message);
}
@ -138,11 +118,8 @@ namespace Microsoft.AspNetCore.Hosting.Tests
var serviceCollection = new ServiceCollection();
var services = serviceCollection.BuildServiceProvider();
var diagnosticMessages = new List<string>();
var hostingEnv = new HostingEnvironment { EnvironmentName = "WithNullConfigureServices" };
var loader = new StartupLoader(services, hostingEnv);
var type = loader.FindStartupType("Microsoft.AspNetCore.Hosting.Tests", diagnosticMessages);
var startup = loader.LoadMethods(type, diagnosticMessages);
var type = StartupLoader.FindStartupType("Microsoft.AspNetCore.Hosting.Tests", "WithNullConfigureServices");
var startup = StartupLoader.LoadMethods(services, type, "WithNullConfigureServices");
var app = new ApplicationBuilder(services);
app.ApplicationServices = startup.ConfigureServicesDelegate(new ServiceCollection());
@ -157,11 +134,8 @@ namespace Microsoft.AspNetCore.Hosting.Tests
var serviceCollection = new ServiceCollection();
var services = serviceCollection.BuildServiceProvider();
var diagnosticMessages = new List<string>();
var hostingEnv = new HostingEnvironment { EnvironmentName = "WithConfigureServices" };
var loader = new StartupLoader(services, hostingEnv);
var type = loader.FindStartupType("Microsoft.AspNetCore.Hosting.Tests", diagnosticMessages);
var startup = loader.LoadMethods(type, diagnosticMessages);
var type = StartupLoader.FindStartupType("Microsoft.AspNetCore.Hosting.Tests", "WithConfigureServices");
var startup = StartupLoader.LoadMethods(services, type, "WithConfigureServices");
var app = new ApplicationBuilder(services);
app.ApplicationServices = startup.ConfigureServicesDelegate(serviceCollection);
@ -177,10 +151,8 @@ namespace Microsoft.AspNetCore.Hosting.Tests
var serviceCollection = new ServiceCollection();
var services = serviceCollection.BuildServiceProvider();
var diagnosticMessages = new List<string>();
var hostingEnv = new HostingEnvironment();
var loader = new StartupLoader(services, hostingEnv);
var startup = loader.LoadMethods(typeof(TestStartup), diagnosticMessages);
var startup = StartupLoader.LoadMethods(services, typeof(TestStartup), hostingEnv.EnvironmentName);
var app = new ApplicationBuilder(services);
app.ApplicationServices = startup.ConfigureServicesDelegate(serviceCollection);
@ -196,10 +168,7 @@ namespace Microsoft.AspNetCore.Hosting.Tests
var serviceCollection = new ServiceCollection();
var services = serviceCollection.BuildServiceProvider();
var diagnosticMessages = new List<string>();
var hostingEnv = new HostingEnvironment { EnvironmentName = "No" };
var loader = new StartupLoader(services, hostingEnv);
var startup = loader.LoadMethods(typeof(TestStartup), diagnosticMessages);
var startup = StartupLoader.LoadMethods(services, typeof(TestStartup), "No");
var app = new ApplicationBuilder(services);
app.ApplicationServices = startup.ConfigureServicesDelegate(serviceCollection);
@ -245,4 +214,4 @@ namespace Microsoft.AspNetCore.Hosting.Tests
_configurationMethodCalledList.Add(instance);
}
}
}
}

View File

@ -28,7 +28,8 @@ namespace Microsoft.AspNetCore.Hosting
var host = (WebHost)builder.UseStartup("MyStartupAssembly").Build();
Assert.Equal("MyStartupAssembly", host.StartupAssemblyName);
Assert.Equal("MyStartupAssembly", host.Options.ApplicationName);
Assert.Equal("MyStartupAssembly", host.Options.StartupAssembly);
}
[Fact]
@ -454,7 +455,7 @@ namespace Microsoft.AspNetCore.Hosting
.Build();
var hostingEnv = host.Services.GetService<IHostingEnvironment>();
Assert.Equal("Microsoft.AspNetCore.Hosting.Tests", hostingEnv.ApplicationName);
Assert.Equal("Microsoft.AspNetCore.Hosting.Tests.NonExistent", hostingEnv.ApplicationName);
var appEnv = host.Services.GetService<IApplicationEnvironment>();
Assert.Equal(PlatformServices.Default.Application.ApplicationName, appEnv.ApplicationName);
Assert.Equal(PlatformServices.Default.Application.ApplicationBasePath, appEnv.ApplicationBasePath);
@ -471,7 +472,7 @@ namespace Microsoft.AspNetCore.Hosting
.Build();
var hostingEnv = host.Services.GetService<IHostingEnvironment>();
Assert.Equal("Microsoft.AspNetCore.Hosting.Tests", hostingEnv.ApplicationName);
Assert.Equal("Microsoft.AspNetCore.Hosting.Tests.NonExistent", hostingEnv.ApplicationName);
var appEnv = host.Services.GetService<IApplicationEnvironment>();
Assert.Equal(PlatformServices.Default.Application.ApplicationName, appEnv.ApplicationName);
Assert.Equal(PlatformServices.Default.Application.ApplicationBasePath, appEnv.ApplicationBasePath);

View File

@ -25,7 +25,8 @@ namespace Microsoft.AspNetCore.Hosting.Tests
{
{ "webroot", "wwwroot"},
{ "server", "Microsoft.AspNetCore.Server.Kestrel"},
{ "application", "MyProjectReference"},
{ "applicationName", "MyProjectReference"},
{ "startupAssembly", "MyProjectReference" },
{ "environment", "Development"},
{ "detailederrors", "true"},
{ "captureStartupErrors", "true" }
@ -35,7 +36,8 @@ namespace Microsoft.AspNetCore.Hosting.Tests
Assert.Equal("wwwroot", config.WebRoot);
Assert.Equal("Microsoft.AspNetCore.Server.Kestrel", config.ServerAssembly);
Assert.Equal("MyProjectReference", config.Application);
Assert.Equal("MyProjectReference", config.ApplicationName);
Assert.Equal("MyProjectReference", config.StartupAssembly);
Assert.Equal("Development", config.Environment);
Assert.True(config.CaptureStartupErrors);
Assert.True(config.DetailedErrors);

View File

@ -10,10 +10,8 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Fakes;
using Microsoft.AspNetCore.Hosting.Internal;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Hosting.Startup;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Configuration;
@ -228,7 +226,7 @@ namespace Microsoft.AspNetCore.Hosting
var builder = CreateBuilder()
.ConfigureServices(services =>
{
services.AddTransient<IStartupLoader, TestLoader>();
services.AddTransient<IStartup, TestStartup>();
})
.UseServer(this)
.UseStartup("Microsoft.AspNetCore.Hosting.Tests");
@ -496,14 +494,14 @@ namespace Microsoft.AspNetCore.Hosting
}
}
private class TestLoader : IStartupLoader
private class TestStartup : IStartup
{
public Type FindStartupType(string startupAssemblyName, IList<string> diagnosticMessages)
public void Configure(IApplicationBuilder app)
{
throw new NotImplementedException();
}
public StartupMethods LoadMethods(Type startupType, IList<string> diagnosticMessages)
public IServiceProvider ConfigureServices(IServiceCollection services)
{
throw new NotImplementedException();
}
@ -653,4 +651,4 @@ namespace Microsoft.AspNetCore.Hosting
public string TraceIdentifier { get; set; }
}
}
}
}

View File

@ -628,4 +628,4 @@ namespace Microsoft.AspNetCore.TestHost
}
}
}
}
}