Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implemented IHostedService in an asp.net core app, How to run it without the first request on IIS?

I've implemented IHostedService in an asp.net core website. It works great but the problem is that I want it to be started when the hosting server boots up or the IIS service restarts but it won't unless the first request to the website comes in.

  • The website is hosted on IIS version 10.0.18
  • The AppPool is in "AlwaysRunning" mode
  • "PreloadEnabled" is "True" on the website.
  • Setting ".NET CLR Version" to "No Managed Code" or "v4.0.xxxxxx" did not helped.
  • dotnet core version is 2.2 and dotnet core hosting bundle is installed.

UPDATE 1: "Application Initialization Module", Suggested by @Arthur did not help. Neither on site nor server level.

The configuration I used:

    <applicationInitialization
         doAppInitAfterRestart="true"
         skipManagedModules="false"
         remapManagedRequestsTo="init.htm">
        <add initializationPage="/init.htm" hostName="localhost"/>
    </applicationInitialization>

UPDATE 2: Here is how I implemented the interface

internal class PaymentQueueService : IHostedService, IDisposable
{
    private readonly ILogger _logService;
    private Timer _timerEnqueue;

    public PaymentQueueService(ILogger logService)
    {
        _logService = logService;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logService.LogInformation("Starting processing payments.");

        _timerEnqueue = new Timer(EnqueuePayments, null, TimeSpan.Zero,
            TimeSpan.FromSeconds(10));

        return Task.CompletedTask;
    }

    private void EnqueuePayments(object state)
    {
        _logService.LogInformation("Enqueueing Payments.");
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _logService.LogInformation("Stopping processing payments.");

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

        return Task.CompletedTask;
    }

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

The Program class in main.cs file:

public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args).ConfigureServices(services =>
            {
                services.AddHostedService<PaymentQueueService>();
            }).Configure((IApplicationBuilder app) =>
            {

                app.UseMvc();
            })
                .UseStartup<Startup>();
    }

The Startup class:

public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {

        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostEnvironment env)
        {

        }
    }
like image 405
desmati Avatar asked Nov 13 '19 06:11

desmati


1 Answers

Since the suggested "Application Initialization Module" did not work, you could consider making the call yourself with a client since

The module starts the process for the ASP.NET Core app when the first request arrives and restarts the app if it shuts down or crashes.

public class Program {
    static Lazy<HttpClient> client = new Lazy<HttpClient>();
    public static async Task Main(string[] args) {
        var host = CreateWebHostBuilder(args).Start();//non blocking start
        using (host) {
            bool started = false;
            do {
                var response = await client.Value.GetAsync("site root");
                started = response.IsSuccessStatusCode;
                await Task.Delay(someDelayHere);
            } while (!started);
            
            host.WaitForShutdown();
        }
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureServices(services => {
                services.AddHostedService<PaymentQueueService>();
            })
            .Configure((IApplicationBuilder app) => {
                app.UseMvc();
            })
            .UseStartup<Startup>();
}

NOTE:

To prevent apps hosted out-of-process from timing out, use either of the following approaches:

  • Ping the app from an external service in order to keep it running.
  • If the app only hosts background services, avoid IIS hosting and use a Windows Service to host the ASP.NET Core app.
like image 107
Nkosi Avatar answered Nov 12 '22 12:11

Nkosi