3

I have an API which contains a HostedService that is built in VS2022 with .Net 6.

When I run locally the service is called as expected and everything works fine but when deplyed, the service doesn't seem to start.

I have tried many different configurations and even tried using a background service but it all has the same result. Here is my code:

I have an existing app build in VS2019 .Net Core 3.1 that has a HostedService and is working fine. I noticed that when I converted my .Net Core app to .Net 6, the service did not start when I deployed so I decided to just build a little app to try and find what's causing the issue.

Program.cs

using HostedServices.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Host.UseSerilog((context, loggerConfiguration) => loggerConfiguration
        .ReadFrom.Configuration(context.Configuration)
        .Enrich.FromLogContext()
        .Enrich.WithMachineName());

// Add services to the container.

builder.Services.AddControllers();

builder.Services.AddHostedService<MainService>();
var app = builder.Build();

// Configure the HTTP request pipeline.

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Here is the Hosted Service

namespace HostedServices.Services
{
    public class MainService : IHostedService, IDisposable
    {
        private int executionCount = 0;
        private readonly ILogger<MainService> _logger;
        private Timer _timer;
        private Task _executingTask;
        private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource();

        public MainService(ILogger<MainService> logger)
        {
            _logger = logger;
        }
        public Task StartAsync(CancellationToken cancellationToken)
        {
   
            _logger.LogInformation($"Test Hosted Service Started {DateTime.Now}.");
            _timer = new Timer(DoWork, null, TimeSpan.Zero,
                    TimeSpan.FromMinutes(1));

            return Task.CompletedTask;

        }

        private void DoWork(object state)
        {
            _executingTask = DoWorkAsync(_stoppingCts.Token);
        }

        private async Task DoWorkAsync(CancellationToken token)
        {
            _logger.LogInformation(
                    "Doing work: {0}", DateTime.Now);

        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("Service is stopping.");

            _timer?.Change(Timeout.Infinite, 0);

            return Task.CompletedTask;
        }

        public void Dispose()
        {
            _timer?.Dispose();
        }


    }
}

Everything runs fine locally but when I deploy it, the service doesn't seem to start, no log files are generated and I can't seem to find any errors reported.

Any ideas?

tdean
  • 508
  • 1
  • 7
  • 15
  • How are you hosting this when deployed? E.g. IIS on a VM, Azure App Service, Window Service? Do you actually have API endpoints within the app as well? – sellotape Apr 27 '22 at 19:27
  • I am hosting in IIS, no I don't have an endpoint at this time. I was just trying to keep it as minimal as possible so I just have the hosted service. I do have another project that does have endpoints with the hosted service and I do notice that when I call the endpoint I see log entries that show that the service did start but it does not continue to run. – tdean Apr 28 '22 at 15:37
  • 1
    Under IIS, nothing in your app will run until the first request arrives. This includes your `IHostedService`, so if you don't have an endpoint to hit, it will never start. Once it's started you're then subject to the IIS App Pool's idle timeout (and whatever other IIS configuration applies). – sellotape Apr 29 '22 at 09:19
  • Yes, after lots of testing and going back to my .NET Core app, I realized that until your app/site is called then the service won't start. I was under the impression that once your app starts in IIS then this would trigger the hosted service but I guess it's not until your app gets a request.. Thank you so much for your response. I think I will get rid of the hosted service and just make it an endpoint and have a scheduled task call it. – tdean Apr 29 '22 at 13:50
  • 1
    `IHostedService` is quite generic; you can host in other ways as well; e.g. a [Windows Service](https://learn.microsoft.com/en-us/dotnet/core/extensions/windows-service) (pretty easily). – sellotape Apr 29 '22 at 15:16
  • 1
    Just wanted to post an update. I decided to split out my functionality and create a windows service which will call my API. This structure gave me the control I needed over the hosted service. The real issue is that the API doesn't start until a request is made and I needed something to start as soon as it was deployed. Thank you all for your help. – tdean May 20 '22 at 16:12
  • I am using .Net6.0 on Linux and Docker and I suspect I have the same problem - hosted service doesn't get started when deployed. I do have endpoint that gets hit every 5 seconds from kubernetes (api/health) – kuskmen Sep 09 '22 at 12:30

2 Answers2

1

The problem is that while IHostedService start when the site starts when does IIS start the site? By default, it doesn't until a request is made. If your site goes idle for a while IIS will kill the thread as well.

IIS has a feature you can enable called "Application Initialization". You then need to configure the Application Pool to always start (startMode="AlwaysRunning").

Lastly, you configure the site's application to preload which sends a fake HTTP request to the site on startup (preloadEnabled="true").

The docs for this are here: https://learn.microsoft.com/en-us/iis/get-started/whats-new-in-iis-8/iis-80-application-initialization

Bryan Dam
  • 23
  • 5
0

We had the same problem and resolved it by following IIS settings:

  • Under "Page" Advanced Settings: "Preload Enabled" must be set to "true" (this forces app to run after every app pool recycle and on startup)
  • Under "App Pool" Advanced settings: "Start mode" must be set to "AlwaysRunning"
  • "Page" in IIS must have http allowed (since preload enabled works only on http and not via https)
Lonsarg
  • 21
  • 3