如何在 ASP.NET Core 2.1 中的计时器上运行 BackgroundService

S. *_*nke 15 c# background-process background-service asp.net-core asp.net-core-2.1

我想在 ASP.NET Core 2.1 中运行后台作业。它必须每 2 小时运行一次,并且需要访问我的 DI 容器,因为它将在数据库中执行一些清理工作。它需要async并且应该独立于我的 ASP.NET Core 2.1 应用程序运行。

我看到有一个IHostedService,但 ASP.NET Core 2.1 还引入了一个名为的抽象类BackgroundService,它为我做了更多的工作。看起来不错,我想用它!

不过,我一直无法弄清楚如何运行源自BackgroundService计时器的服务。

我是否需要ExecuteAsync(token)通过记住它上次运行的时间并确定这是否是 2 小时来配置它,或者是否有更好/更清晰的方法来说明它必须每 2 小时运行一次?

另外,我解决问题的方法是否正确BackgroundService

谢谢!

编辑:

将此发布在MS 扩展存储库上

S. *_*nke 18

2020 年 4 月更新,在底部阅读!

@Panagiotis Kanavos 在我的问题的评论中给出了答案,但它没有将其作为实际答案发布;这个答案是献给他/她的。

我使用了一个Timed 后台服务,比如 Microsoft docs 中的那个服务来创建服务。

internal class TimedHostedService : IHostedService, IDisposable
{
    private readonly ILogger _logger;
    private Timer _timer;

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

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Timed Background Service is starting.");

        _timer = new Timer(DoWork, null, TimeSpan.Zero, 
            TimeSpan.FromSeconds(5));

        return Task.CompletedTask;
    }

    private void DoWork(object state)
    {
        _logger.LogInformation("Timed Background Service is working.");
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Timed Background Service is stopping.");

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

        return Task.CompletedTask;
    }

    public void Dispose()
    {
        _timer?.Dispose();
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的情况下,我_timer通过执行new Timer(async () => await DoWorkAsync(), ...).

将来,可以编写一个扩展,在 Extensions 存储库中提供这样的类,因为我认为这非常有用。我在描述中发布了 github 问题链接。

提示,如果您计划将此类重用于多个托管服务,请考虑创建一个包含计时器和抽象类的基类,PerformWork()以便“时间”逻辑仅在一个地方。

谢谢您的回答!我希望这对未来的人有所帮助。

04-2020 更新

使用开箱即用的普通 Core 服务集合 DI 容器,在此处注入范围服务是不可能的。我正在使用 autofac 这使得IClassRepository由于注册错误而可以在构造函数中使用范围服务,但是当我开始处理一个仅使用的不同项目时,AddScoped<>(), AddSingleton<>(), AddTransient<>()我们发现注入范围的东西不起作用,因为您不在范围内语境。

为了使用你的范围服务,注入一个IServiceScopeFactory(更容易测试)并使用CreateScope()它允许你使用scope.GetService()一个using语句:)

  • 这不会使计时器异步,“new Timer(async () =&gt; wait DoWorkAsync()”默认情况下,计时器将按定时计划执行您的函数,无论其他函数是否仍在执行。此外,您没有任何保证如果没有发出请求,计时器将执行,请参阅此[问题](/sf/ask/3769121051/)我对计时器有同样的误解,答案解释了这一点 (2认同)