.net core 重写IHostedService以在所有任务完成后停止

.net core 重写IHostedService以在所有任务完成后停止,.net-core,background-process,.net-5,asp.net-core-hosted-services,ihostedservice,.net Core,Background Process,.net 5,Asp.net Core Hosted Services,Ihostedservice,我有一个应用程序,通常应该是一个简单的控制台应用程序,可以作为windows任务调度器不时调用的计划任务进行编程 该程序应该在两个数据库上启动一些更新,每个数据库一个服务。假设contosososervice应该更新ContosoDatabase 最后,它被编写成一个.NET核心应用程序,使用IHostedServices作为服务的基础,这可能不是最好的选择,如下所示: public class ContosoService : IHostedService { private read

我有一个应用程序,通常应该是一个简单的控制台应用程序,可以作为windows任务调度器不时调用的计划任务进行编程

该程序应该在两个数据库上启动一些更新,每个数据库一个服务。假设contosososervice应该更新ContosoDatabase

最后,它被编写成一个.NET核心应用程序,使用IHostedServices作为服务的基础,这可能不是最好的选择,如下所示:

public class ContosoService : IHostedService {
    private readonly ILogger<ContosoService> _log;
    private readonly IContosoRepository _repository;
    
    private Task executingTask;

    public ContosoService(
        ILogger<ContosoService> log,
        IContosoRepository repository,
        string mode) {
        _log = log;
        _repository = repository;
    }

    public Task StartAsync(CancellationToken cancellationToken) {
        _log.LogInformation(">>> {serviceName} started <<<", nameof(ContosoService));
        executingTask = ExcecuteAsync(cancellationToken);

        // If the task is completed then return it, 
        // this should bubble cancellation and failure to the caller
        if (executingTask.IsCompleted)
            return executingTask;

        // Otherwise it's running
        // >> don't want it to run!
        // >> it should end after all task finished!
        return Task.CompletedTask;
    }

    private async Task<bool> ExcecuteAsync(CancellationToken cancellationToken) {
        var myUsers = _repository.GetMyUsers();

        if (myUsers == null || myUsers.Count() == 0) {
            _log.LogWarning("{serviceName} has any entry to process, will stop", this.GetType().Name);
            return false;
        }
        else {
            // on mets à jour la liste des employés Agresso obtenue
            await _repository.UpdateUsersAsync(myUsers);
        }

        _log.LogInformation(">>> {serviceName} finished its tasks <<<", nameof(ContosoService));
        return true;
    }

    public Task StopAsync(CancellationToken cancellationToken) {
        _log.LogInformation(">>> {serviceName} stopped <<<", nameof(ContosoService));
        return Task.CompletedTask;
    }
}
public static void Main(string[] args)
{
    try {
        CreateHostBuilder(args).Build().Run();
    }
    catch (Exception ex) {
        Log.Fatal(ex, ">>> the application could not start <<<");
    }
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host
    .CreateDefaultBuilder(args)
    .ConfigureServices((hostContext, services) => {
        var config = hostContext.Configuration;
        
        if (args.Contains("Alonso")) {
            services
            .AddHostedService(provider =>
                new AlonsoService(
                    provider.GetService<ILogger<AlonsoService>>(),
                    provider.GetService<IAlonsoRepository>()));
        }

        // if there also Cedig in the list, they can be run in parallel
        if (args.Contains("Contoso")) {
            services
            .AddHostedService(provider =>
                new ContosoService(
                    provider.GetService<ILogger<ContosoService>>(),
                    provider.GetService<IContosoRepository>()));
        }
    });

但这似乎没有帮助:在所有任务完成后,应用程序仍在运行。

托管服务是后台服务。另一种方法是:它们可以对应用程序的启动和停止事件做出反应,从而可以优雅地结束。它们并不意味着在完成时停止主应用程序,它们可能和应用程序一样长

我想说,简单的任务和等待所有的任务会让你得到更好的服务。或者在后台作业完成工作后发送一些事件,并在main中处理它们


无论您选择什么触发器,都可以通过注入IHostApplicationLifetime并对其调用StopApplication方法来停止.net应用程序。在早期版本中,它只是IApplicationLifetime。

在主机关闭之前,查看方法运行不会停止。似乎StopAsync没有停止服务。so Environment.Exit0;从未联系过。可能用于强制结束主机,或注入Environment.Exit0;在ContosoService类中,如果可能的话,即使不是最优的。

根据@Maxim的建议,我通过注入IHostApplicationLifetime和lastService布尔值发现了这个肮脏但有效的解决方法:


我如何从我的服务向主应用发送消息以停止它?或者,更确切地说,我想在所有服务完成后停止应用,如何执行?IHostApplicationLifetime完成了任务,我刚刚从上一个执行服务spasibo bol'shoe中删除了应用;使用CancellationTocker强制停止主机,这将非常好。。。现在想知道如何从应用程序内部执行此操作…请查看我自己的答案,谢谢并感谢@maxim:!我知道您解决了这个问题,但我注意到StopAsync没有在任何地方被调用。我想如果它放在同一个地方而不是_hostApplicationLifetime.stopplication;它将产生相同的结果。应用程序或主机在尝试停止应用程序时似乎调用了StopAsync。我有来自StopAsync的日志,在StopApplication调用之后传递感谢您提供的信息。很高兴你的问题解决了。
public static void Main(string[] args) {
    try {
        CreateHostBuilder(filteredArgs.ToArray()).Build().Run();                
    }
    catch (Exception ex) {
        //Log....
    }

    Environment.Exit(0); // here
}
public ConsosoService(
    IHostApplicationLifetime hostApplicationLifetime,
    // ...
    bool lastService) 
{ ... }

public async Task StartAsync(CancellationToken cancellationToken)
{
    // do the job

    if (_lastService)
        _hostApplicationLifetime.StopApplication(); 
    // stops the application and cancel/stops other services as well
}