Reputation: 5837
I have a Windows Service that I wrote in C#. This "brains" of the app are in a class named Driver
. This class asynchronously starts up a bunch of threads to do a number of things. There's nothing "blocking" in this code. The Driver is responsible for starting these threads, listening for specific events, and stopping the threads on these events.
At this time, I've wrapped this code as a Windows Service. The OnStart
, OnStop
, etc methods are just wrappers into this class. The Windows Service runs as I want it to. However, now, I want to run this code in a Docker container. My question is, how do I modify this code to become a long running process instead of a Windows Service.
My initial thought was to wrap my code in a Console app. Then, I would move the code from OnStart
to the body of the Console app. After that code, I would just have a while(true){}
statement to keep the console app alive. However, this feels like a hack.
Is this approach a hack? Or, is there a "real" way to do this? If so, what is the recommended approach for having a long running C# app in Docker?
Upvotes: 1
Views: 2804
Reputation: 4554
Since you want to run your app as Docker container, I guess you also want to run it on Linux. Then I think you can target your code to .NET Core which supports to run on both Windows and Linux (also Mac).
There's a package named Microsoft.Extensions.Hosting
which is a hosting and startup infrastructures for both asp.net core and dotnet core console application, it helps to manage your process to life cycle. Its ConsoleLifetime
class would process the general aplication start/stop method which defined in the interface named IHostedService
.
You just need to implement your own IHostedService
(or inherit from the BackgroundService
class) and add it to host context within ConfigureServies
.
Here's a sample hosted service:
public class TimedHostedService : IHostedService, IDisposable
{
private readonly ILogger _logger;
private Timer _timer;
public TimedHostedService(ILogger<TimedHostedService> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is starting.");
_timer = new Timer(DoWork, null, TimeSpan.Zero,
TimeSpan.FromSeconds(5));
return Task.CompletedTask;
}
private void DoWork(object state)
{
_logger.LogInformation("Timed Background Service is working.");
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is stopping.");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
Then modify your main method:
public class Program
{
public static async Task Main(string[] args)
{
var hostBuilder = new HostBuilder()
// Add configuration, logging, ...
.ConfigureServices((hostContext, services) =>
{
// Add your services with depedency injection.
services.AddSingleton<IHostedService, TimedHostedService>();
});
await hostBuilder.RunConsoleAsync();
}
}
Upvotes: 3
Reputation: 5584
Using dotnet core 2.1 you can use the IHostedService known from aspnet in a console application as well, provided by Microsoft.Extensions.Hosting. Combine that with C# >= 7.1 (check your project settings), where you can use an async Main method, your code would look something like this:
static async Task Main(string[] args)
{
var builder = new HostBuilder(); // from Microsoft.Extensions.Hosting
builder.ConfigureServices(s => s.AddSingleton<IHostedService, YourImplementation>());
await builder.RunConsoleAsync();
}
Upvotes: 5
Reputation: 4399
A hosting process (e.g. a Windows Service) is better than a console application that runs in a tight loop (while(true) {}
). Since you intend to run it in a Docker container, I'd suggest looking at AspNet Core, targeting .Net Core so that you can use a Linux base image (which has a smaller image size). AspNet Core provides a web server (Kestrel) which will be the hosting process in this case.
To run a long running task in AspNet Core, have a look at Asp.Net core long running task.
Upvotes: 1