Here is sample of windows service behaviour where hosted service is trying to connect before application started I have a .net core 2.2 console application configured to run as a windows service. I also configured a hosted service that tries to establish a connection to an external tcp server indefinitely until connection is successful.
The issue is when the windows service is started and external tcp server is unavailable, windows service is hung in the 'starting' state until the external service becomes available to connect. The service never reaches to 'running' state. Though this behavior is consistent with documentation https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.2&tabs=visual-studio#ihostedservice-interface , which says that "When using the Generic Host, StartAsync is called before ApplicationStarted is triggered.", what I want is to begin the hosted service after windows service fully started (i.e in 'running' state).
I did look into the aspect of making the hosted service a timed background service with an initial delay enough to start the windows service fully instead registering the hosted service with IApplicationLifetime.ApplicationStarted but it felt like a hacky way of estimating the time required for the windows service to start. Instead I am looking for an elegant way to begin the hosted service after windows service fully started.
Code for Hosted Service
public class TcpHostedService : BackgroundService
{
private readonly IPostBridgeConnection _client;
private readonly IApplicationLifetime _lifetime;
private readonly ILogger<TcpHostedService> _logger;
public TcpHostedService(IPostBridgeConnection client,
IApplicationLifetime lifetime,
ILogger<TcpHostedService> logger)
{
_client = client ?? throw new ArgumentNullException(nameof(client));
_lifetime = lifetime ?? throw new ArgumentNullException(nameof(lifetime));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("tcp connection - registering start and stop events with application lifetime");
_lifetime.ApplicationStarted.Register(() => OnStarted(stoppingToken));
_lifetime.ApplicationStopping.Register(() => OnStopping(stoppingToken));
_lifetime.ApplicationStopped.Register(() => OnStopped(stoppingToken));
_logger.LogInformation("tcp connection - registered start and stop events with application lifetime");
_logger.LogInformation("tcp connection backgroundService is starting");
return Task.CompletedTask;
}
#region lifetime events
private void OnStarted(CancellationToken t)
{
_logger.LogInformation("tcp connection onstarted event triggered");
_client.Init(t);
}
private void OnStopping(CancellationToken t)
{
_logger.LogInformation("tcp connection onstopping event triggered");
_client.Dispose(t);
}
private void OnStopped(CancellationToken t)
{
_logger.LogInformation("tcp connection onstopped event triggered");
_logger.LogInformation("tcp connection is disposed");
}
This service is registered in Program.cs as
services.AddHostedService<TcpHostedService>();
The way you have shown, registering a callback with the IApplicationLifetime.ApplicationStarted
token (or IHostApplicationLifetime
in .NET Core 3.0) looks like it should work. The limitation there is that the callback is synchronous, so you shouldn't be doing anything async
in there.
However, personally, I would prefer the approach you described initially - make the IHostedService
a BackgroundService
so that it doesn't block startup, and use Polly to retry any transient errors until the client is able to connect.