如何实现仅运行一次的.net core IHostedService

8

我知道一个只运行一次的IHostedService听起来像一个控制台应用程序,但我想使用它而不是普通的控制台应用程序的原因是:

  • .net core引入了通用主机(Generic Host)来运行非 HTTP 应用程序。
  • 普通的控制台应用程序没有 DI 、Logger 和 Configurations 等功能可供使用。

通过使用以下代码,我能够在某种程度上实现这种一次性行为,然而我找不到一个优雅的方式在应用程序完成后退出。

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

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureServices((hostContext, services) => { services.AddHostedService<StartUp>(); });
    }

StartUp 是一个简单的 IHostedService

public class StartUp:IHostedService
    {
        private ILogger<StartUp> _logger;

        public StartUp(ILogger<StartUp> logger)
        {
            _logger = logger;
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("start async");
            return Task.CompletedTask;
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("stop async");
            return Task.CompletedTask;
        }
    }

如何优雅地停止应用程序?或者如果这样完全是错误的,我应该如何实现一次性应用程序?

4个回答

13

是的,您可以通过将IHostApplicationLifetime注入到托管服务中来实现。

例如:

 public class StartUp:IHostedService
 {
    private readonly IHostApplicationLifetime _host;
    private ILogger<StartUp> _logger;

    public StartUp(IHostApplicationLifetime host, ILogger<StartUp> logger)
    {
        _host = host;
        _logger = logger;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("start async");
        _host.StopApplication();
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("stop async");
        return Task.CompletedTask;
    }
}

你可以通过设置Environment.ExitCode来设置退出代码。例如:
Environment.ExitCode = 0;
_host.StopApplication();

11

如果你只是为了弥补缺失的依赖注入(DI)和ILogger而使用IHostedService,那么你也可以直接使用ILoggerIConfiguration设置依赖注入,而无需使用IHostedService

public class Program
{
    public static async Task Main(string[] args)
    {
        var configBuilder = new ConfigurationBuilder()
                                .AddJsonFile("appsettings.json", optional: true);
        var config = configBuilder.Build();

        var sp = new ServiceCollection()
            .AddLogging(b => b.AddConsole())
            .AddSingleton<IConfiguration>(config)
            .AddSingleton<IFooService, FooService>()
            .BuildServiceProvider();

        var logger = sp.GetService<ILoggerFactory>().CreateLogger<Program>();
        logger.LogDebug("Starting");

        var bar = sp.GetService<IFooService>();
        await bar.DoAsync();
    }
}

通过此设置,您的代码仅运行一次,可以解决您在 ServiceCollection 中注册的所有服务,并且无需 Host 即可启动。

示例:.netfiddle


有没有一种方法可以使用这样的配置实现优雅的关闭?您可以轻松地使用IHostedService来实现,但是如果使用带有DI和所有这些功能的简单控制台应用程序,则无法实现。 - osynavets

3

1

另一种实现方式可能是使用IHostBuilder,但不调用run方法。

    static Task Main(string[] args)
    {
        using IHost host = CreateHostBuilder(args).Build();

        var manager = host.Services.GetRequiredService<IManager>();
        manager.DoSomething();

        // we don't have registered hosted services therefore we don't call host.RunAsync();
        return Task.CompletedTask; // or change signature to void and remove return
    }

    static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureServices((_, services) =>
                services
                .AddScoped<IFoo, Foo>()
                .AddScoped<IBar, Bar>()
                .AddSingleton<IManager, Manager>());

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接