Execute IHostingStart instances in the primary assembly (#1033)
* #1000 Execute IHostingStart instances in the primary assembly Also make the sample app runnable. Add an opt-out flag, more tests
This commit is contained in:
parent
d2816d14ab
commit
8377d226f1
18
Hosting.sln
18
Hosting.sln
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26228.4
|
||||
VisualStudioVersion = 15.0.26418.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E0497F39-AFFB-4819-A116-E39E361915AB}"
|
||||
EndProject
|
||||
|
|
@ -31,6 +30,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Hostin
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Hosting.TestSites", "test\Microsoft.AspNetCore.Hosting.TestSites\Microsoft.AspNetCore.Hosting.TestSites.csproj", "{542D4600-B232-4B17-A08C-E31EBFA0D74E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestStartupAssembly1", "test\TestStartupAssembly1\TestStartupAssembly1.csproj", "{39D3B138-37DB-4D03-A5A0-3F2B02EFC671}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -165,6 +166,18 @@ Global
|
|||
{542D4600-B232-4B17-A08C-E31EBFA0D74E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{542D4600-B232-4B17-A08C-E31EBFA0D74E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{542D4600-B232-4B17-A08C-E31EBFA0D74E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{39D3B138-37DB-4D03-A5A0-3F2B02EFC671}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{39D3B138-37DB-4D03-A5A0-3F2B02EFC671}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{39D3B138-37DB-4D03-A5A0-3F2B02EFC671}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{39D3B138-37DB-4D03-A5A0-3F2B02EFC671}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{39D3B138-37DB-4D03-A5A0-3F2B02EFC671}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{39D3B138-37DB-4D03-A5A0-3F2B02EFC671}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{39D3B138-37DB-4D03-A5A0-3F2B02EFC671}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{39D3B138-37DB-4D03-A5A0-3F2B02EFC671}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{39D3B138-37DB-4D03-A5A0-3F2B02EFC671}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{39D3B138-37DB-4D03-A5A0-3F2B02EFC671}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{39D3B138-37DB-4D03-A5A0-3F2B02EFC671}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{39D3B138-37DB-4D03-A5A0-3F2B02EFC671}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -181,5 +194,6 @@ Global
|
|||
{03148731-EA95-40A2-BAE8-A12315EA1748} = {E0497F39-AFFB-4819-A116-E39E361915AB}
|
||||
{FC578F4E-171C-4F82-B301-3ABF6318D082} = {FEB39027-9158-4DE2-997F-7ADAEF8188D0}
|
||||
{542D4600-B232-4B17-A08C-E31EBFA0D74E} = {FEB39027-9158-4DE2-997F-7ADAEF8188D0}
|
||||
{39D3B138-37DB-4D03-A5A0-3F2B02EFC671} = {FEB39027-9158-4DE2-997F-7ADAEF8188D0}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<Project>
|
||||
<ItemGroup>
|
||||
<ExcludeFromTest Include="$(RepositoryRoot)test\Microsoft.AspNetCore.Hosting.TestSites\Microsoft.AspNetCore.Hosting.TestSites.csproj" />
|
||||
<ExcludeFromTest Include="$(RepositoryRoot)test\TestStartupAssembly1\TestStartupAssembly1.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace SampleStartups
|
||||
{
|
||||
// We can't reference real servers in this sample without creating a circular repo dependency.
|
||||
// This fake server lets us at least run the code.
|
||||
public class FakeServer : IServer
|
||||
{
|
||||
public IFeatureCollection Features => new FeatureCollection();
|
||||
|
||||
public Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static class FakeServerWebHostBuliderExtensions
|
||||
{
|
||||
public static IWebHostBuilder UseFakeServer(this IWebHostBuilder builder)
|
||||
{
|
||||
return builder.ConfigureServices(services => services.AddSingleton<IServer, FakeServer>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net46;netcoreapp2.0</TargetFrameworks>
|
||||
<StartupObject>SampleStartups.StartupInjection</StartupObject>
|
||||
<OutputType>exe</OutputType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ namespace SampleStartups
|
|||
|
||||
var host = new WebHostBuilder()
|
||||
.UseConfiguration(config)
|
||||
.UseFakeServer()
|
||||
.UseStartup<StartupBlockingOnStart>()
|
||||
.Build();
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ namespace SampleStartups
|
|||
|
||||
var host = new WebHostBuilder()
|
||||
.UseConfiguration(config)
|
||||
.UseFakeServer()
|
||||
.UseStartup<StartupConfigureAddresses>()
|
||||
.UseUrls("http://localhost:5000", "http://localhost:5001")
|
||||
.Build();
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ namespace SampleStartups
|
|||
{
|
||||
_host = new WebHostBuilder()
|
||||
//.UseKestrel()
|
||||
.UseFakeServer()
|
||||
.UseStartup<StartupExternallyControlled>()
|
||||
.Start(_urls.ToArray());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ namespace SampleStartups
|
|||
|
||||
var host = new WebHostBuilder()
|
||||
.UseConfiguration(config) // Default set of configurations to use, may be subsequently overridden
|
||||
//.UseKestrel()
|
||||
//.UseKestrel()
|
||||
.UseFakeServer()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory()) // Override the content root with the current directory
|
||||
.UseUrls("http://*:1000", "https://*:902")
|
||||
.UseEnvironment("Development")
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ namespace SampleStartups
|
|||
public static void Main(string[] args)
|
||||
{
|
||||
var host = new WebHostBuilder()
|
||||
//.UseKestrel()
|
||||
//.UseKestrel()
|
||||
.UseFakeServer()
|
||||
.UseStartup<StartupHelloWorld>()
|
||||
.Build();
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
using System;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
// HostingStartup's in the primary assembly are run automatically.
|
||||
[assembly: HostingStartup(typeof(SampleStartups.StartupInjection))]
|
||||
|
||||
namespace SampleStartups
|
||||
{
|
||||
public class StartupInjection : IHostingStartup
|
||||
{
|
||||
public void Configure(IWebHostBuilder builder)
|
||||
{
|
||||
builder.UseStartup<InjectedStartup>();
|
||||
}
|
||||
|
||||
// Entry point for the application.
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var host = new WebHostBuilder()
|
||||
//.UseKestrel()
|
||||
.UseFakeServer()
|
||||
// Each of these three sets ApplicationName to the current assembly, which is needed in order to
|
||||
// scan the assembly for HostingStartupAttributes.
|
||||
// .UseSetting(WebHostDefaults.ApplicationKey, "SampleStartups")
|
||||
// .Configure(_ => { })
|
||||
.UseStartup<NormalStartup>()
|
||||
.Build();
|
||||
|
||||
host.Run();
|
||||
}
|
||||
}
|
||||
|
||||
public class NormalStartup
|
||||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
Console.WriteLine("NormalStartup.ConfigureServices");
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
Console.WriteLine("NormalStartup.Configure");
|
||||
app.Run(async (context) =>
|
||||
{
|
||||
await context.Response.WriteAsync("Hello World!");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class InjectedStartup
|
||||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
Console.WriteLine("InjectedStartup.ConfigureServices");
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
Console.WriteLine("InjectedStartup.Configure");
|
||||
app.Run(async (context) =>
|
||||
{
|
||||
await context.Response.WriteAsync("Hello World!");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ namespace Microsoft.AspNetCore.Hosting
|
|||
public static readonly string ServerUrlsKey = "urls";
|
||||
public static readonly string ContentRootKey = "contentRoot";
|
||||
public static readonly string PreferHostingUrlsKey = "preferHostingUrls";
|
||||
public static readonly string PreventHostingStartupKey = "preventHostingStartup";
|
||||
|
||||
public static readonly string ShutdownTimeoutKey = "shutdownTimeoutSeconds";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,10 @@ namespace Microsoft.AspNetCore.Hosting.Internal
|
|||
Environment = configuration[WebHostDefaults.EnvironmentKey];
|
||||
WebRoot = configuration[WebHostDefaults.WebRootKey];
|
||||
ContentRootPath = configuration[WebHostDefaults.ContentRootKey];
|
||||
HostingStartupAssemblies = configuration[WebHostDefaults.HostingStartupAssembliesKey]?.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries) ?? new string[0];
|
||||
PreventHostingStartup = ParseBool(configuration, WebHostDefaults.PreventHostingStartupKey);
|
||||
// Search the primary assembly and configured assemblies.
|
||||
HostingStartupAssemblies = $"{ApplicationName};{configuration[WebHostDefaults.HostingStartupAssembliesKey]}"
|
||||
.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries) ?? new string[0];
|
||||
PreferHostingUrls = ParseBool(configuration, WebHostDefaults.PreferHostingUrlsKey);
|
||||
|
||||
var timeout = configuration[WebHostDefaults.ShutdownTimeoutKey];
|
||||
|
|
@ -39,6 +42,8 @@ namespace Microsoft.AspNetCore.Hosting.Internal
|
|||
|
||||
public string ApplicationName { get; set; }
|
||||
|
||||
public bool PreventHostingStartup { get; set; }
|
||||
|
||||
public IReadOnlyList<string> HostingStartupAssemblies { get; set; }
|
||||
|
||||
public bool DetailedErrors { get; set; }
|
||||
|
|
|
|||
|
|
@ -302,36 +302,39 @@ namespace Microsoft.AspNetCore.Hosting
|
|||
services.AddSingleton(loggerFactory);
|
||||
_context.LoggerFactory = loggerFactory;
|
||||
|
||||
var exceptions = new List<Exception>();
|
||||
|
||||
// Execute the hosting startup assemblies
|
||||
foreach (var assemblyName in _options.HostingStartupAssemblies)
|
||||
if (!_options.PreventHostingStartup)
|
||||
{
|
||||
try
|
||||
{
|
||||
var assembly = Assembly.Load(new AssemblyName(assemblyName));
|
||||
var exceptions = new List<Exception>();
|
||||
|
||||
foreach (var attribute in assembly.GetCustomAttributes<HostingStartupAttribute>())
|
||||
// Execute the hosting startup assemblies
|
||||
foreach (var assemblyName in _options.HostingStartupAssemblies)
|
||||
{
|
||||
try
|
||||
{
|
||||
var hostingStartup = (IHostingStartup)Activator.CreateInstance(attribute.HostingStartupType);
|
||||
hostingStartup.Configure(this);
|
||||
var assembly = Assembly.Load(new AssemblyName(assemblyName));
|
||||
|
||||
foreach (var attribute in assembly.GetCustomAttributes<HostingStartupAttribute>())
|
||||
{
|
||||
var hostingStartup = (IHostingStartup)Activator.CreateInstance(attribute.HostingStartupType);
|
||||
hostingStartup.Configure(this);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Capture any errors that happen during startup
|
||||
exceptions.Add(new InvalidOperationException($"Startup assembly {assemblyName} failed to execute. See the inner exception for more details.", ex));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Capture any errors that happen during startup
|
||||
exceptions.Add(new InvalidOperationException($"Startup assembly {assemblyName} failed to execute. See the inner exception for more details.", ex));
|
||||
}
|
||||
}
|
||||
|
||||
if (exceptions.Count > 0)
|
||||
{
|
||||
hostingStartupErrors = new AggregateException(exceptions);
|
||||
|
||||
// Throw directly if we're not capturing startup errors
|
||||
if (!_options.CaptureStartupErrors)
|
||||
if (exceptions.Count > 0)
|
||||
{
|
||||
throw hostingStartupErrors;
|
||||
hostingStartupErrors = new AggregateException(exceptions);
|
||||
|
||||
// Throw directly if we're not capturing startup errors
|
||||
if (!_options.CaptureStartupErrors)
|
||||
{
|
||||
throw hostingStartupErrors;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Hosting\Microsoft.AspNetCore.Hosting.csproj" />
|
||||
<ProjectReference Include="..\TestStartupAssembly1\TestStartupAssembly1.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -833,13 +833,30 @@ namespace Microsoft.AspNetCore.Hosting
|
|||
{
|
||||
var builder = CreateWebHostBuilder()
|
||||
.CaptureStartupErrors(false)
|
||||
.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, typeof(WebHostBuilderTests).GetTypeInfo().Assembly.FullName)
|
||||
.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, typeof(TestStartupAssembly1.TestHostingStartup1).GetTypeInfo().Assembly.FullName)
|
||||
.Configure(app => { })
|
||||
.UseServer(new TestServer());
|
||||
|
||||
using (var host = builder.Build())
|
||||
{
|
||||
Assert.Equal("1", builder.GetSetting("testhostingstartup"));
|
||||
Assert.Equal("1", builder.GetSetting("testhostingstartup1"));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_RunsHostingStartupRunsPrimaryAssemblyFirst()
|
||||
{
|
||||
var builder = CreateWebHostBuilder()
|
||||
.CaptureStartupErrors(false)
|
||||
.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, typeof(TestStartupAssembly1.TestHostingStartup1).GetTypeInfo().Assembly.FullName)
|
||||
.Configure(app => { })
|
||||
.UseServer(new TestServer());
|
||||
|
||||
using (var host = builder.Build())
|
||||
{
|
||||
Assert.Equal("0", builder.GetSetting("testhostingstartup"));
|
||||
Assert.Equal("1", builder.GetSetting("testhostingstartup1"));
|
||||
Assert.Equal("01", builder.GetSetting("testhostingstartup_chain"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -871,7 +888,6 @@ namespace Microsoft.AspNetCore.Hosting
|
|||
{
|
||||
var builder = CreateWebHostBuilder()
|
||||
.CaptureStartupErrors(false)
|
||||
.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, typeof(WebHostBuilderTests).GetTypeInfo().Assembly.FullName)
|
||||
.Configure(app =>
|
||||
{
|
||||
var loggerFactory = app.ApplicationServices.GetService<ILoggerFactory>();
|
||||
|
|
@ -888,12 +904,26 @@ namespace Microsoft.AspNetCore.Hosting
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_DoesNotRunHostingStartupAssembliesDoNotRunIfNotSpecified()
|
||||
public void Build_DoesRunHostingStartupFromPrimaryAssemblyEvenIfNotSpecified()
|
||||
{
|
||||
var builder = CreateWebHostBuilder()
|
||||
.Configure(app => { })
|
||||
.UseServer(new TestServer());
|
||||
|
||||
using (builder.Build())
|
||||
{
|
||||
Assert.Equal("0", builder.GetSetting("testhostingstartup"));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_HostingStartupFromPrimaryAssemblyCanBeDisabled()
|
||||
{
|
||||
var builder = CreateWebHostBuilder()
|
||||
.UseSetting(WebHostDefaults.PreventHostingStartupKey, "true")
|
||||
.Configure(app => { })
|
||||
.UseServer(new TestServer());
|
||||
|
||||
using (builder.Build())
|
||||
{
|
||||
Assert.Null(builder.GetSetting("testhostingstartup"));
|
||||
|
|
@ -1027,7 +1057,8 @@ namespace Microsoft.AspNetCore.Hosting
|
|||
public void Configure(IWebHostBuilder builder)
|
||||
{
|
||||
var loggerProvider = new TestLoggerProvider();
|
||||
builder.UseSetting("testhostingstartup", "1")
|
||||
builder.UseSetting("testhostingstartup", "0")
|
||||
.UseSetting("testhostingstartup_chain", builder.GetSetting("testhostingstartup_chain") + "0")
|
||||
.ConfigureServices(services => services.AddSingleton<ServiceA>())
|
||||
.ConfigureServices(services => services.AddSingleton<ITestSink>(loggerProvider.Sink))
|
||||
.ConfigureLogging(lf => lf.AddProvider(loggerProvider));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
// 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 Microsoft.AspNetCore.Hosting;
|
||||
|
||||
[assembly: HostingStartup(typeof(TestStartupAssembly1.TestHostingStartup1))]
|
||||
|
||||
namespace TestStartupAssembly1
|
||||
{
|
||||
public class TestHostingStartup1 : IHostingStartup
|
||||
{
|
||||
public void Configure(IWebHostBuilder builder)
|
||||
{
|
||||
builder.UseSetting("testhostingstartup1", "1");
|
||||
builder.UseSetting("testhostingstartup_chain", builder.GetSetting("testhostingstartup_chain") + "1");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Import Project="..\..\build\common.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netcoreapp2.0;net46</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Hosting.Abstractions\Microsoft.AspNetCore.Hosting.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Loading…
Reference in New Issue