web-dev-qa-db-fra.com

Implémenté IHostedService dans une application principale asp.net, comment l'exécuter sans la première demande sur IIS?

J'ai implémenté IHostedService dans un site Web principal asp.net. Cela fonctionne très bien, mais le problème est que je veux qu'il soit démarré lorsque le serveur d'hébergement démarre ou que le service IIS redémarre, mais il ne le sera pas sauf si la première demande au site Web arrive.

  • Le site Web est hébergé sur IIS version 10.0.18
  • L'AppPool est en mode "AlwaysRunning"
  • "PreloadEnabled" est "True" sur le site Web.
  • La définition de ".NET CLR Version" sur "No Managed Code" ou "v4.0.xxxxxx" n'a pas aidé.
  • la version dotnet core est 2.2 et le pack d'hébergement dotnet core est installé.

MISE À JOUR 1: "Module d'initialisation d'application", suggéré par @Arthur n'a pas aidé. Ni sur site ni au niveau du serveur.

La configuration que j'ai utilisée:

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

PDATE 2: Voici comment j'ai implémenté l'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();
    }
}

La classe Program dans le fichier main.cs:

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>();
    }

La classe de démarrage:

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)
        {

        }
    }
6
desmati

Étant donné que le "module d'initialisation d'application" suggéré ne fonctionnait pas, vous pourriez envisager de passer l'appel vous-même avec un client, car

Le module démarre le processus pour l'application ASP.NET Core lorsque la première demande arrive et redémarre l'application si elle s'arrête ou se bloque.

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>();
}

REMARQUE:

Pour empêcher les applications hébergées hors processus de se terminer, utilisez l'une des approches suivantes:

  • Envoyez une requête ping à l'application depuis un service externe afin de la faire fonctionner.
  • Si l'application héberge uniquement des services d'arrière-plan, évitez IIS l'hébergement et utilisez un service Windows pour héberger l'application ASP.NET Core.
1
Nkosi

Voici comment je crée mon webservices. Dans le Startup.cs J'injecte mon IHostedServices. Container est une classe de StructureMap. Je l'ai copié à partir d'un projet existant, il ne correspond donc pas à 100% à votre échantillon.

public class Program
{
    public static void Main(string[] args)
    {
        Config.Directories.EnsureDirectoryTree();

        var isService = !(Debugger.IsAttached || args.Contains("--console"));
        var webHostService = MyWebHostService.BuildWebHostService(args.Where(arg => arg != "--console").ToArray());

        if (isService)
        {
            ServiceBase.Run(webHostService);
        }
        else
        {
            webHostService.InitializeBackend();
            webHostService.Host.Run();
        }
    }
}

public class MyWebHostService : WebHostService
{
    public static readonly Logger Logger = LogManager.GetCurrentClassLogger();

    public IWebHost Host { get; }

    public static ZlsWebHostService BuildWebHostService(string[] args)
    {
        ConfigureNLog();

        Logger.Info("{0} (v{1}) starting...", Config.ServiceName, GetApplicationVersion());

        // restore config files
        Config.Files.EnsureRestored();

        var Host = CreateWebHostBuilder(args).Build();
        return new ZlsWebHostService(Host);
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args)
    {
        var pathToExe = Assembly.GetExecutingAssembly().Location;
        var pathToContentRoot = Path.GetDirectoryName(pathToExe);

        return WebHost.CreateDefaultBuilder()
            .UseKestrel()
            .UseContentRoot(pathToContentRoot)
            .ConfigureAppConfiguration((context, config) =>
            {
                config.SetBasePath(Config.Directories.ActiveConfig);
                config.AddJsonFile(Config.Files.KestrelConfig.RelativePath, true, true);})
            .ConfigureLogging((hostingContext, logging) =>
            {
                logging.ClearProviders();
                logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
                logging.AddConsole();
                if(hostingContext.HostingEnvironment.IsDevelopment())
                    logging.AddDebug();
            })
            .UseNLog()
            .UseStartup<Startup>();
    }

    public MyWebHostService(IWebHost Host) : base(Host)
    {
        this.Host = Host;
    }

    protected override void OnStarting(string[] args)
    {
        InitializeBackend();

        base.OnStarting(args);
    }

    protected override void OnStarted()
    {
        Logger.Info("{0} started...", Config.ServiceName);
        base.OnStarted();
    }

    protected override void OnStopped()
    {
        Logger.Info("{0} stopped...", Config.ServiceName);
        base.OnStopped();
    }

...

}

public class Startup
{
...

    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.Microsoft.com/fwlink/?LinkID=398940
    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        ...

        services.AddSingleton<PaymentQueueService>();

        ...

        var container = new Container(c =>
        {
            c.IncludeRegistry<MyFooRegistry>();
            c.Populate(services);
        });
        return container.GetInstance<IServiceProvider>();
    }

...

}
0
Dominic Jonas

L'héberger "OutOfProcess" fait l'affaire, mais gardez à l'esprit que ce n'est pas la manière préférée. J'ai créé un dépôt sur GitHub avec le code donné et vous pouvez vérifier la modification lors du dernier commit.

Source: https://github.com/tvdias/SO58831661

Pour une explication approfondie sur les différences entre InProcess et OutOfProcess, je recommande de lire cette page: https://weblog.west-wind.com/posts/2019/Mar/16/ASPNET-Core-Hosting-on-IIS -avec-ASPNET-Core-22 .

0
tvdias