0

我有一个应用程序,它通常应该是一个简单的控制台应用程序,被编程为 Windows 任务调度程序不时调用的计划任务。

该程序应该在两个数据库上启动一些更新,每个数据库一个服务。说ContosoDatabase应该由ContosoService.

最后,它被编写为一个 .NET Core 应用程序,使用s 作为服务的基础,这可能不是最佳选择,如下所示:IHostedService

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

我像这样从 main 调用它:

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

现在,问题肯定是,一旦所有更新完成,应用程序就不会停止。

有没有办法快速重写应用程序以使其在第二个服务完成其任务后停止?

我试图Environment.Exit(0);

public static void Main(string[] args) {
    try {
        CreateHostBuilder(filteredArgs.ToArray()).Build().Run();                
    }
    catch (Exception ex) {
        //Log....
    }

    Environment.Exit(0); // here
}

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

4

4 回答 4

2

HostedServices 是后台服务。反之亦然:它们可以对应用程序的启动和停止事件做出反应,以便它们可以优雅地结束。它们并不意味着在完成后停止您的主应用程序,它们可能与应用程序一样长。

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

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

于 2021-02-09T10:57:23.587 回答
1

查看IHost 接口文档,方法 run() 在主机关闭之前不会停止。似乎 StopAsync() 没有停止服务。所以 Environment.Exit(0); 从未达到。可能使用CancellationToken强制结束主机,或者注入 Environment.Exit(0); 如果可能,在 ContosoService 类中,即使不是最佳的。

于 2021-02-09T16:07:25.520 回答
1

按照@Maxim 的建议,我通过注入IHostApplicationLifetimelastService布尔值发现了这个肮脏但可行的解决方法:

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
}
于 2021-02-09T17:39:11.737 回答
0

这是另一种不需要创建托管服务的方法

using var host = CreateHostBuilder(args).Build();
await host.StartAsync();
using var scope = host.Services.CreateScope();
var worker = scope.ServiceProvider.GetService<Worker>();
await worker!.Run();
await host.StopAsync();

IHostBuilder CreateHostBuilder(string[] args) =>
     Host.CreateDefaultBuilder(args)
         .ConfigureServices(services => ConfigureServices(services));

void ConfigureServices(IServiceCollection services)
{
    //main class which does the work
    services.AddScoped<Worker>();
    //do some DB operations
    services.AddScoped<DbCtxt>();
}

完整代码https://github.com/raghavan-mk/dotnet/tree/main/DIInConsole

于 2021-11-20T16:14:53.883 回答