首页 > 解决方案 > 如何在 ASP.NET Core 2.1 中的计时器上运行 BackgroundService

问题描述

我想在 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 扩展存储库上

标签: c#asp.net-corebackground-processasp.net-core-2.1background-service

解决方案


04-2020更新,在底部阅读!

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

我使用了类似 Microsoft 文档中的定时后台服务来创建该服务。

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

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

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

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

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

04-2020 更新

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

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


推荐阅读