There appear to be two types of substantive .NET Core project: ASP.NET Core Web App and Console App. I would like to build something like a Windows Service in a Docker environment (Linux container), where the process starts, runs indefinitely, and stops only when told to. Neither project type seems suitable. Am I missing something?
3 Answers
Both types of applications make sense, it depends how you plan on communicating with this service.
If you want to communicate with it over standard HTTP on some TCP port, then using an ASP.Net Core Web Application will make things easier.
If you want to communicate over something a bit more "exotic" like RabbitMQ, Kafka, raw TCP sockets or something else, then a Console Application is what you want. The trick, as Gareth Luckett's answer points out, is to just make sure that your main
function blocks. A running Docker container expects the main thread to block as long as the container should be running.

- 678
- 4
- 7
The term "console" might be a bit misleading here. Microsoft uses it to distinguish it from "GUI" apps (like WinForms, WPF, UWP, Xamarin etc.) or web applications that are brought through IIS. ASP.NET Core applications are just console apps with libraries to host a web server.
So for your app, a "console" is the project type you want. As has been mentioned by @mason, even Windows Services are just "console" applications - an .exe file that isn't a GUI application.

- 94,744
- 25
- 252
- 217
Unfortunately as a console application requires a stdin when running,through docker it will exit right away. You can 'host' it using asp.net.
public class Program
{
public static ManualResetEventSlim Done = new ManualResetEventSlim(false);
public static void Main(string[] args)
{
//This is unbelievably complex because .NET Core Console.ReadLine() does not block in a docker container...!
var host = new WebHostBuilder().UseStartup(typeof(Startup)).Build();
using (CancellationTokenSource cts = new CancellationTokenSource())
{
Action shutdown = () =>
{
if (!cts.IsCancellationRequested)
{
Console.WriteLine("Application is shutting down...");
cts.Cancel();
}
Done.Wait();
};
Console.CancelKeyPress += (sender, eventArgs) =>
{
shutdown();
// Don't terminate the process immediately, wait for the Main thread to exit gracefully.
eventArgs.Cancel = true;
};
host.Run(cts.Token);
Done.Set();
}
}
}
The Startup class:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IServer, ConsoleAppRunner>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
}
}
The ConsoleAppRunner class
public class ConsoleAppRunner : IServer
{
/// <summary>A collection of HTTP features of the server.</summary>
public IFeatureCollection Features { get; }
public ConsoleAppRunner(ILoggerFactory loggerFactory)
{
Features = new FeatureCollection();
}
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
public void Dispose()
{
}
/// <summary>Start the server with an application.</summary>
/// <param name="application">An instance of <see cref="T:Microsoft.AspNetCore.Hosting.Server.IHttpApplication`1" />.</param>
/// <typeparam name="TContext">The context associated with the application.</typeparam>
public void Start<TContext>(IHttpApplication<TContext> application)
{
//Actual program code starts here...
Console.WriteLine("Demo app running...");
Program.Done.Wait(); // <-- Keeps the program running - The Done property is a ManualResetEventSlim instance which gets set if someone terminates the program.
}
}

- 896
- 7
- 13