diff --git a/Hosting.sln b/Hosting.sln
index f9325585a1..2128f4ea5f 100644
--- a/Hosting.sln
+++ b/Hosting.sln
@@ -55,6 +55,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
Directory.Build.targets = Directory.Build.targets
EndProjectSection
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GenericHostSample", "samples\GenericHostSample\GenericHostSample.csproj", "{8529E5F7-059F-4A06-AD6E-ECDF4F4838FE}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Hosting", "src\Microsoft.Extensions.Hosting\Microsoft.Extensions.Hosting.csproj", "{1DA77D55-5DB9-4426-87DC-758579335944}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GenericWebHost", "samples\GenericWebHost\GenericWebHost.csproj", "{FCDD1C82-623C-4779-8A9C-B0D827CEA8BF}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Hosting.Tests", "test\Microsoft.Extensions.Hosting.Tests\Microsoft.Extensions.Hosting.Tests.csproj", "{45E296BB-7628-49AF-B5A5-04CD9A89CAD3}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -237,6 +245,54 @@ Global
{F894D8C5-B760-4734-AD31-3CA6FC557CCF}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{F894D8C5-B760-4734-AD31-3CA6FC557CCF}.Release|x86.ActiveCfg = Release|Any CPU
{F894D8C5-B760-4734-AD31-3CA6FC557CCF}.Release|x86.Build.0 = Release|Any CPU
+ {8529E5F7-059F-4A06-AD6E-ECDF4F4838FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8529E5F7-059F-4A06-AD6E-ECDF4F4838FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8529E5F7-059F-4A06-AD6E-ECDF4F4838FE}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {8529E5F7-059F-4A06-AD6E-ECDF4F4838FE}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {8529E5F7-059F-4A06-AD6E-ECDF4F4838FE}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {8529E5F7-059F-4A06-AD6E-ECDF4F4838FE}.Debug|x86.Build.0 = Debug|Any CPU
+ {8529E5F7-059F-4A06-AD6E-ECDF4F4838FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8529E5F7-059F-4A06-AD6E-ECDF4F4838FE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8529E5F7-059F-4A06-AD6E-ECDF4F4838FE}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {8529E5F7-059F-4A06-AD6E-ECDF4F4838FE}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {8529E5F7-059F-4A06-AD6E-ECDF4F4838FE}.Release|x86.ActiveCfg = Release|Any CPU
+ {8529E5F7-059F-4A06-AD6E-ECDF4F4838FE}.Release|x86.Build.0 = Release|Any CPU
+ {1DA77D55-5DB9-4426-87DC-758579335944}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1DA77D55-5DB9-4426-87DC-758579335944}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1DA77D55-5DB9-4426-87DC-758579335944}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {1DA77D55-5DB9-4426-87DC-758579335944}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {1DA77D55-5DB9-4426-87DC-758579335944}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {1DA77D55-5DB9-4426-87DC-758579335944}.Debug|x86.Build.0 = Debug|Any CPU
+ {1DA77D55-5DB9-4426-87DC-758579335944}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1DA77D55-5DB9-4426-87DC-758579335944}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1DA77D55-5DB9-4426-87DC-758579335944}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {1DA77D55-5DB9-4426-87DC-758579335944}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {1DA77D55-5DB9-4426-87DC-758579335944}.Release|x86.ActiveCfg = Release|Any CPU
+ {1DA77D55-5DB9-4426-87DC-758579335944}.Release|x86.Build.0 = Release|Any CPU
+ {FCDD1C82-623C-4779-8A9C-B0D827CEA8BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FCDD1C82-623C-4779-8A9C-B0D827CEA8BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FCDD1C82-623C-4779-8A9C-B0D827CEA8BF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {FCDD1C82-623C-4779-8A9C-B0D827CEA8BF}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {FCDD1C82-623C-4779-8A9C-B0D827CEA8BF}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {FCDD1C82-623C-4779-8A9C-B0D827CEA8BF}.Debug|x86.Build.0 = Debug|Any CPU
+ {FCDD1C82-623C-4779-8A9C-B0D827CEA8BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FCDD1C82-623C-4779-8A9C-B0D827CEA8BF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FCDD1C82-623C-4779-8A9C-B0D827CEA8BF}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {FCDD1C82-623C-4779-8A9C-B0D827CEA8BF}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {FCDD1C82-623C-4779-8A9C-B0D827CEA8BF}.Release|x86.ActiveCfg = Release|Any CPU
+ {FCDD1C82-623C-4779-8A9C-B0D827CEA8BF}.Release|x86.Build.0 = Release|Any CPU
+ {45E296BB-7628-49AF-B5A5-04CD9A89CAD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {45E296BB-7628-49AF-B5A5-04CD9A89CAD3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {45E296BB-7628-49AF-B5A5-04CD9A89CAD3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {45E296BB-7628-49AF-B5A5-04CD9A89CAD3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {45E296BB-7628-49AF-B5A5-04CD9A89CAD3}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {45E296BB-7628-49AF-B5A5-04CD9A89CAD3}.Debug|x86.Build.0 = Debug|Any CPU
+ {45E296BB-7628-49AF-B5A5-04CD9A89CAD3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {45E296BB-7628-49AF-B5A5-04CD9A89CAD3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {45E296BB-7628-49AF-B5A5-04CD9A89CAD3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {45E296BB-7628-49AF-B5A5-04CD9A89CAD3}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {45E296BB-7628-49AF-B5A5-04CD9A89CAD3}.Release|x86.ActiveCfg = Release|Any CPU
+ {45E296BB-7628-49AF-B5A5-04CD9A89CAD3}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -258,6 +314,10 @@ Global
{EDFF02F0-A8A4-4EB1-A179-94D7500FB266} = {FA7D2012-C1B4-4AF7-9ADD-381B2004EA16}
{58194285-5891-464A-A96B-0FE043029E8A} = {FA7D2012-C1B4-4AF7-9ADD-381B2004EA16}
{F894D8C5-B760-4734-AD31-3CA6FC557CCF} = {FA7D2012-C1B4-4AF7-9ADD-381B2004EA16}
+ {8529E5F7-059F-4A06-AD6E-ECDF4F4838FE} = {9C7520A0-F2EB-411C-8BB2-80B39C937217}
+ {1DA77D55-5DB9-4426-87DC-758579335944} = {E0497F39-AFFB-4819-A116-E39E361915AB}
+ {FCDD1C82-623C-4779-8A9C-B0D827CEA8BF} = {9C7520A0-F2EB-411C-8BB2-80B39C937217}
+ {45E296BB-7628-49AF-B5A5-04CD9A89CAD3} = {FEB39027-9158-4DE2-997F-7ADAEF8188D0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {AABD536D-E05F-409B-A716-535E0C478076}
diff --git a/samples/GenericHostSample/GenericHostSample.csproj b/samples/GenericHostSample/GenericHostSample.csproj
new file mode 100644
index 0000000000..2f60b84b2e
--- /dev/null
+++ b/samples/GenericHostSample/GenericHostSample.csproj
@@ -0,0 +1,24 @@
+
+
+
+ netcoreapp2.0;net461
+ GenericHostSample.ProgramHelloWorld
+ Exe
+ latest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/GenericHostSample/MyContainer.cs b/samples/GenericHostSample/MyContainer.cs
new file mode 100644
index 0000000000..11556a845e
--- /dev/null
+++ b/samples/GenericHostSample/MyContainer.cs
@@ -0,0 +1,6 @@
+namespace GenericHostSample
+{
+ internal class MyContainer
+ {
+ }
+}
\ No newline at end of file
diff --git a/samples/GenericHostSample/MyContainerFactory.cs b/samples/GenericHostSample/MyContainerFactory.cs
new file mode 100644
index 0000000000..d4fd399d77
--- /dev/null
+++ b/samples/GenericHostSample/MyContainerFactory.cs
@@ -0,0 +1,18 @@
+using System;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace GenericHostSample
+{
+ internal class MyContainerFactory : IServiceProviderFactory
+ {
+ public MyContainer CreateBuilder(IServiceCollection services)
+ {
+ return new MyContainer();
+ }
+
+ public IServiceProvider CreateServiceProvider(MyContainer containerBuilder)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/GenericHostSample/MyServiceA.cs b/samples/GenericHostSample/MyServiceA.cs
new file mode 100644
index 0000000000..93be796bbb
--- /dev/null
+++ b/samples/GenericHostSample/MyServiceA.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Hosting;
+
+namespace GenericHostSample
+{
+ public class MyServiceA : IHostedService
+ {
+ private bool _stopping;
+ private Task _backgroundTask;
+
+ public Task StartAsync(CancellationToken cancellationToken)
+ {
+ Console.WriteLine("MyServiceA is starting.");
+ _backgroundTask = BackgroundTask();
+ return Task.CompletedTask;
+ }
+
+ private async Task BackgroundTask()
+ {
+ while (!_stopping)
+ {
+ await Task.Delay(TimeSpan.FromSeconds(5));
+ Console.WriteLine("MyServiceA is doing background work.");
+ }
+
+ Console.WriteLine("MyServiceA background task is stopping.");
+ }
+
+ public async Task StopAsync(CancellationToken cancellationToken)
+ {
+ Console.WriteLine("MyServiceA is stopping.");
+ _stopping = true;
+ if (_backgroundTask != null)
+ {
+ // TODO: cancellation
+ await _backgroundTask;
+ }
+ }
+ }
+}
diff --git a/samples/GenericHostSample/MyServiceB.cs b/samples/GenericHostSample/MyServiceB.cs
new file mode 100644
index 0000000000..a143758b90
--- /dev/null
+++ b/samples/GenericHostSample/MyServiceB.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Hosting;
+
+namespace GenericHostSample
+{
+ public class MyServiceB : IHostedService
+ {
+ private bool _stopping;
+ private Task _backgroundTask;
+
+ public Task StartAsync(CancellationToken cancellationToken)
+ {
+ Console.WriteLine("MyServiceB is starting.");
+ _backgroundTask = BackgroundTask();
+ return Task.CompletedTask;
+ }
+
+ private async Task BackgroundTask()
+ {
+ while (!_stopping)
+ {
+ await Task.Delay(TimeSpan.FromSeconds(7));
+ Console.WriteLine("MyServiceB is doing background work.");
+ }
+
+ Console.WriteLine("MyServiceB background task is stopping.");
+ }
+
+ public async Task StopAsync(CancellationToken cancellationToken)
+ {
+ Console.WriteLine("MyServiceB is stopping.");
+ _stopping = true;
+ if (_backgroundTask != null)
+ {
+ // TODO: cancellation
+ await _backgroundTask;
+ }
+ }
+ }
+}
diff --git a/samples/GenericHostSample/ProgramExternallyControlled.cs b/samples/GenericHostSample/ProgramExternallyControlled.cs
new file mode 100644
index 0000000000..749442b161
--- /dev/null
+++ b/samples/GenericHostSample/ProgramExternallyControlled.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+
+namespace GenericHostSample
+{
+ public class ProgramExternallyControlled
+ {
+ private IHost _host;
+
+ public ProgramExternallyControlled()
+ {
+ _host = new HostBuilder()
+ .UseServiceProviderFactory(new MyContainerFactory())
+ .ConfigureContainer((hostContext, container) =>
+ {
+ })
+ .ConfigureAppConfiguration((hostContext, config) =>
+ {
+ config.AddEnvironmentVariables();
+ config.AddJsonFile("appsettings.json", optional: true);
+ })
+ .ConfigureServices((hostContext, services) =>
+ {
+ services.AddScoped();
+ services.AddScoped();
+ })
+ .Build();
+ }
+
+ public void Start()
+ {
+ _host.Start();
+ }
+
+ public async Task StopAsync()
+ {
+ await _host.StopAsync(TimeSpan.FromSeconds(5));
+ _host.Dispose();
+ }
+ }
+}
diff --git a/samples/GenericHostSample/ProgramFullControl.cs b/samples/GenericHostSample/ProgramFullControl.cs
new file mode 100644
index 0000000000..59400077bb
--- /dev/null
+++ b/samples/GenericHostSample/ProgramFullControl.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+
+namespace GenericHostSample
+{
+ public class ProgramFullControl
+ {
+ public static async Task Main(string[] args)
+ {
+ var host = new HostBuilder()
+ .UseServiceProviderFactory(new MyContainerFactory())
+ .ConfigureContainer((hostContext, container) =>
+ {
+ })
+ .ConfigureAppConfiguration((hostContext, config) =>
+ {
+ config.AddEnvironmentVariables();
+ config.AddJsonFile("appsettings.json", optional: true);
+ config.AddCommandLine(args);
+ })
+ .ConfigureServices((hostContext, services) =>
+ {
+ services.AddScoped();
+ services.AddScoped();
+ })
+ .Build();
+
+ var s = host.Services;
+
+ using (host)
+ {
+ Console.WriteLine("Starting!");
+
+ await host.StartAsync();
+
+ Console.WriteLine("Started! Press to stop.");
+
+ Console.ReadLine();
+
+ Console.WriteLine("Stopping!");
+
+ await host.StopAsync();
+
+ Console.WriteLine("Stopped!");
+ }
+ }
+ }
+}
diff --git a/samples/GenericHostSample/ProgramHelloWorld.cs b/samples/GenericHostSample/ProgramHelloWorld.cs
new file mode 100644
index 0000000000..a145a2534d
--- /dev/null
+++ b/samples/GenericHostSample/ProgramHelloWorld.cs
@@ -0,0 +1,21 @@
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+
+namespace GenericHostSample
+{
+ public class ProgramHelloWorld
+ {
+ public static async Task Main(string[] args)
+ {
+ var builder = new HostBuilder()
+ .ConfigureServices((hostContext, services) =>
+ {
+ services.AddScoped();
+ services.AddScoped();
+ });
+
+ await builder.RunConsoleAsync();
+ }
+ }
+}
diff --git a/samples/GenericHostSample/ServiceBaseControlled.cs b/samples/GenericHostSample/ServiceBaseControlled.cs
new file mode 100644
index 0000000000..431e061be5
--- /dev/null
+++ b/samples/GenericHostSample/ServiceBaseControlled.cs
@@ -0,0 +1,23 @@
+#if NET461
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+
+namespace GenericHostSample
+{
+ public class ServiceBaseControlled
+ {
+ public static async Task Main(string[] args)
+ {
+ var builder = new HostBuilder()
+ .ConfigureServices((hostContext, services) =>
+ {
+ services.AddScoped();
+ services.AddScoped();
+ });
+
+ await builder.RunAsServiceAsync();
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/samples/GenericHostSample/ServiceBaseLifetime.cs b/samples/GenericHostSample/ServiceBaseLifetime.cs
new file mode 100644
index 0000000000..08bf4348d0
--- /dev/null
+++ b/samples/GenericHostSample/ServiceBaseLifetime.cs
@@ -0,0 +1,64 @@
+#if NET461
+using System;
+using System.ServiceProcess;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+
+namespace GenericHostSample
+{
+ public static class ServiceBaseLifetimeHostExtensions
+ {
+ public static IHostBuilder UseServiceBaseLifetime(this IHostBuilder hostBuilder)
+ {
+ return hostBuilder.ConfigureServices((hostContext, services) => services.AddSingleton());
+ }
+
+ public static Task RunAsServiceAsync(this IHostBuilder hostBuilder, CancellationToken cancellationToken = default)
+ {
+ return hostBuilder.UseServiceBaseLifetime().Build().RunAsync(cancellationToken);
+ }
+ }
+
+ public class ServiceBaseLifetime : ServiceBase, IHostLifetime
+ {
+ private Action