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

     2023-02-19     141

关键词:

【中文标题】如何在 ASP.NET Core 2.1 中的计时器上运行 BackgroundService【英文标题】:How to run BackgroundService on a timer in ASP.NET Core 2.1 【发布时间】:2019-05-12 15:54:03 【问题描述】:

我想在 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 extensions repo

【问题讨论】:

A timed background service 是文档中的示例之一。检查Background tasks with hosted services in ASP.NET Core。 嗯,我明白了。问题是,我看到 DoWork() 不是异步的。我可以标记DoWork async,但这并不是真正正确的方式,因为它不会被等待(?) @PanagiotisKanavos 如果您有答案,请将其写为实际答案,以便当您的答案帮助我解决这个问题时,我可以将其标记为已完成 :) 您能告诉我为什么实现IHostedService 然后使用计时器会比使用BackgroundService 并通过执行计时器检查来检查您是否想在ExecuteAsync 中运行您的工作更好? (再次,发布你的答案+为什么这比这种方法更好的原因作为答案)我知道我的方法会导致ExecuteAsync 被调用,如果它不会被执行,但是我的问题是:什么是如果你不能把它放在计时器上,BackgroundService 的点?追问:那为什么没有TimedBackgroundService呢? 请谨慎使用带计时器的托管服务,因为 IIS 每 20 分钟回收一次,您的托管服务将同时停止。因此,您需要将应用程序池设置为始终开启,这可能会导致泄漏或内存问题。 【参考方案1】:

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

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

我使用Timed background service 就像 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();
    

在我的例子中,我通过 new Timer(async () =&gt; await DoWorkAsync(), ...) 异步调用了 _timer

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

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

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

04-2020 年更新

使用开箱即用的普通核心服务集合 DI 容器无法在此处注入作用域服务。由于注册错误,我使用了 autofac,这使得在构造函数中使用像 IClassRepository 这样的范围服务成为可能,但是当我开始处理一个只使用 AddScoped&lt;&gt;(), AddSingleton&lt;&gt;(), AddTransient&lt;&gt;() 的不同项目时,我们发现注入范围的东西不起作用,因为您不在作用域上下文中。

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

【讨论】:

这不会使计时器异步,new Timer(async () =&gt; await DoWorkAsync() 默认情况下,计时器将按定时计划执行您的函数,而不管其他函数是否仍在执行。此外,如果没有请求,您的计时器将不会执行,请参阅question 我对计时器有同样的误解,答案解释了 对于每个登录到应用程序的用户,后台服务将首先触发,后续触发在上述时间之后 别忘了在 Startup.cs 中使用“services.AddHostedService();”注册服务。【参考方案2】:

实现这一点的一种方法是使用 HangFire.io,它将处理计划的后台任务,管理服务器之间的平衡,并且具有相当大的可扩展性。

在https://www.hangfire.io查看定期工作

【讨论】:

我想补充一点,.NET Core 提供了一个完全免费的解决方案。 HangFire 将根据您的用例花钱。正如我在帖子中提到的,我想使用 ASP.NET Core 的解决方案,因为它存在;我只是不知道如何使用它。为这样的事情使用 3rd 方解决方案似乎有点奇怪,因为我想做的并不复杂。 当然。如果您使用 SQL Server,HangFire 是免费的,它提供了一个完整的解决方案,您可以实施然后继续开发。 (注意:我不隶属于)但确定我理解您的要求,只是想提供帮助。 “如果你使用 SQL 服务器,HangFire 是免费的”。你能提供一个链接吗?此外,我确实重视您的回复,只是它不是我想要使用的 ASP.NET Core 功能的一部分,因此我觉得它不应该成为帖子的答案。 :) hangfire.io/pricing 你在这里 - 第一列“打开” - LGPL 3.0 下的 Hangfire Core,商业用途【参考方案3】:

这是基于之前的回复和https://***.com/a/56666084/1178572的改进版本

改进:

    在前一个任务完成执行之前,它不会启动计时器。这将有助于避免陷入同时执行两个任务的情况。 它支持异步任务 它处理任务执行期间可能出现的异常,以确保它不会阻止下一个任务的执行。 为执行范围的每个任务创建一个范围,因此您可以在 RunJobAsync 中访问任何范围内的服务 您可以在继承的类中指定间隔和初始任务执行时间。

访问范围服务示例

    protected override async Task RunJobAsync(IServiceProvider serviceProvider, CancellationToken stoppingToken)
    
            DbContext context = serviceProvider.GetRequiredService<DbContext>();
    

源代码:

public abstract class TimedHostedService : IHostedService, IDisposable

    private readonly ILogger _logger;
    private Timer _timer;
    private Task _executingTask;
    private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource();

    IServiceProvider _services;
    public TimedHostedService(IServiceProvider services)
    
        _services = services;
        _logger = _services.GetRequiredService<ILogger<TimedHostedService>>();
        
    

    public Task StartAsync(CancellationToken cancellationToken)
    
        _timer = new Timer(ExecuteTask, null,FirstRunAfter, TimeSpan.FromMilliseconds(-1));

        return Task.CompletedTask;
    

    private void ExecuteTask(object state)
    
        _timer?.Change(Timeout.Infinite, 0);
        _executingTask = ExecuteTaskAsync(_stoppingCts.Token);
    

    private async Task ExecuteTaskAsync(CancellationToken stoppingToken)
    
        try
        
            using (var scope = _services.CreateScope())
            
                await RunJobAsync(scope.ServiceProvider, stoppingToken);
            
        
        catch (Exception exception)
        
            _logger.LogError("BackgroundTask Failed", exception);
        
        _timer.Change(Interval, TimeSpan.FromMilliseconds(-1));
    

    /// <summary>
    /// This method is called when the <see cref="IHostedService"/> starts. The implementation should return a task 
    /// </summary>
    /// <param name="serviceProvider"></param>
    /// <param name="stoppingToken">Triggered when <see cref="IHostedService.StopAsync(CancellationToken)"/> is called.</param>
    /// <returns>A <see cref="Task"/> that represents the long running operations.</returns>
    protected abstract Task RunJobAsync(IServiceProvider serviceProvider, CancellationToken stoppingToken);
    protected abstract TimeSpan Interval  get; 
    
    protected abstract TimeSpan FirstRunAfter  get; 
    
    public virtual async Task StopAsync(CancellationToken cancellationToken)
    
        _timer?.Change(Timeout.Infinite, 0);

        // Stop called without start
        if (_executingTask == null)
        
            return;
        

        try
        
            // Signal cancellation to the executing method
            _stoppingCts.Cancel();
        
        finally
        
            // Wait until the task completes or the stop token triggers
            await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken));
        

    

    public void Dispose()
    
        _stoppingCts.Cancel();
        _timer?.Dispose();
    

【讨论】:

如何使用基于声明的授权保护 asp.net core 2.1 中的静态文件夹

】如何使用基于声明的授权保护asp.netcore2.1中的静态文件夹【英文标题】:Howtoprotectstaticfolderinasp.netcore2.1usingclaims-basedauthorization【发布时间】:2018-10-2320:40:06【问题描述】:我有一个使用asp.netcore2.1的小项目。我希望保护充满静... 查看详情

如何在 ASP.NET Core 2.1 中向 Visual Studio 添加新的 Razor 页面模板?

】如何在ASP.NETCore2.1中向VisualStudio添加新的Razor页面模板?【英文标题】:HowdoIaddanewRazorPageTemplatetoVisualStudioinASP.NETCore2.1?【发布时间】:2019-05-1505:05:58【问题描述】:我的项目层次结构中有Templates文件夹,我一直在大量修改这些... 查看详情

如何在 asp.net core 2.1 中使用自定义消息设置状态代码?

】如何在asp.netcore2.1中使用自定义消息设置状态代码?【英文标题】:HowdoIsetthestatuscodewithcustommessageinasp.netcore2.1?【发布时间】:2019-09-0811:21:37【问题描述】:我正在使用2.1版asp.net核心,我创建了一个示例API项目,该项目运行良... 查看详情

如何在 ASP.NET Core 2.1 中使用自动完成输入引发模式

】如何在ASP.NETCore2.1中使用自动完成输入引发模式【英文标题】:HowtoraiseamodalwithanautocompleteinputinASP.NETCore2.1【发布时间】:2020-11-2302:32:09【问题描述】:我需要在ASP-NETCore2.1的模式中实现JQuery自动完成问题是它没有显示任何东西... 查看详情

如何在 ASP.NET Core Web API 2.1 中添加 WCF 服务引用

】如何在ASP.NETCoreWebAPI2.1中添加WCF服务引用【英文标题】:HowtoaddWCFServicereferenceinASP.NETCorewebAPI2.1【发布时间】:2021-03-2618:22:54【问题描述】:我尝试在我的ASP.netCoreWebAPI中添加服务引用,但出现以下错误:Unabletogeneratedeps.json,itmay... 查看详情

如何在 ASP .NET Core 2.1 中将登录页面设为默认路由?

】如何在ASP.NETCore2.1中将登录页面设为默认路由?【英文标题】:HowtomakeLoginpageasadefaultrouteinASP.NETCore2.1?【发布时间】:2019-01-0112:52:25【问题描述】:我是ASP.NETCore2.1的初学者,正在开发使用ASP.NETCore2.1和个人身份验证的项目。我... 查看详情

如何在 ASP.NET Core 2.1 中获取客户端 IP 地址

】如何在ASP.NETCore2.1中获取客户端IP地址【英文标题】:HowtogetClientIPaddressinASP.NETCore2.1【发布时间】:2018-06-3015:57:12【问题描述】:我正在使用MicrosoftVisualStudio2017提供的Angular模板开发ASP.NetCore2.1。我的客户端应用程序运行良好。... 查看详情

ViewData 未传递给 ASP.NET Core 2.1 中的布局

】ViewData未传递给ASP.NETCore2.1中的布局【英文标题】:ViewDataisnotpassedtolayoutinASP.NETCore2.1【发布时间】:2019-03-0901:36:36【问题描述】:已编辑我有一个ASP.NETCore2.1网站,我已经为它的所有标识项搭建了脚手架。脚手架进程在这个位... 查看详情

共享布局视图不适用于 ASP.NET Core 2.1 中的所有视图

】共享布局视图不适用于ASP.NETCore2.1中的所有视图【英文标题】:SharedLayoutviewnotapplyingtoallviewsinASP.NETCore2.1【发布时间】:2019-06-0211:08:59【问题描述】:布局视图未在其他视图之间共享目前我的主页和其他主页共享我的布局视图... 查看详情

ASP.NET Core 2.1 中的数据保护仅适用于一台机器

】ASP.NETCore2.1中的数据保护仅适用于一台机器【英文标题】:DataprotectioninASP.NETCore2.1onlyworksononemachine【发布时间】:2018-11-2310:01:48【问题描述】:我正在使用ASP.NETCoreDataProtectionsystem使用应用程序A加密数据并使用应用程序B解密它... 查看详情

ASP.NET Core 2.1 显示用户和角色

...2019-07-1019:46:24【问题描述】:为了显示用户及其在核心2.1中的角色,我遵循了示例***。我没有错误,但我没有显示用户角色。我认为这可能是我的观点,由于某种原因,我的代码返回了一个电子邮件地址而不是用户角色。Applicati... 查看详情

ASP.Net Core 2.1 中的身份< - 自定义 AccountController

】ASP.NetCore2.1中的身份<-自定义AccountController【英文标题】:IdentityinASP.NetCore2.1<-CustomizeAccountController【发布时间】:2018-11-1313:22:23【问题描述】:我已经安装了ASP.NETCore2.1,但即使我使用ASP.NETCore2.1和IndividualUserAccounts→Storeuser... 查看详情

我如何在 ASP.Net Core 2.1 mvc 应用程序中包含 System.Identitymodel 4.0

】我如何在ASP.NetCore2.1mvc应用程序中包含System.Identitymodel4.0【英文标题】:howcaniincludeSystem.IdentityModel4.0inASP.NetCore2.1mvcapplication【发布时间】:2018-12-2103:13:08【问题描述】:在ASP.NetCore2.1MVC应用程序中实现saml2.0的最佳方法是什么?... 查看详情

ASP.Net Core 2.1 和 IHttpClientFactory 中的 Flurl 客户端生命周期

】ASP.NetCore2.1和IHttpClientFactory中的Flurl客户端生命周期【英文标题】:FlurlclientlifetimeinASP.NetCore2.1andIHttpClientFactory【发布时间】:2018-12-0420:22:09【问题描述】:Flurl指出使用单例客户端是推荐的模式:HttpClient旨在被实例化一次并在... 查看详情

ASP Net Core 2.1 会话

...描述】:我目前正在.NetCore2.1中开发API使用带有Nuxt的Vue2中的客户端应用程序,我在ASP.Net的会话中保存对象时遇到问题。在提出这个问题之前,我已经查看了this和其他链接,但没有任何东西可以帮助我。事实证明,我已经用Postma... 查看详情

Asp.net core 2.1 - 如何服务多个角度应用程序?

】Asp.netcore2.1-如何服务多个角度应用程序?【英文标题】:Asp.netcore2.1-Howtoservemultipleangularapps?【发布时间】:2019-08-1021:23:00【问题描述】:我正在尝试从我的.net核心服务中提供2个Angular应用程序,如下所示:publicvoidConfigureServices... 查看详情

在 ASP.NET Core 2.1 Web API 中实现分页

...905:28:50【问题描述】:我搜索了,但并没有真正找到关于如何在ASP.NETWebAPICore2.1应用程序中实现分页逻辑的文章...我有以下[Route("api/[controller]")][ApiController][EnableCors( 查看详情

ASP.NET Core 2.1 中的 Scaffold Identity UI 并添加全局过滤器

】ASP.NETCore2.1中的ScaffoldIdentityUI并添加全局过滤器【英文标题】:ScaffoldIdentityUIinASP.NETCore2.1andaddGlobalFilter【发布时间】:2019-05-1311:45:19【问题描述】:我有一个ASP.NETCore2.1应用程序,我在其中使用身份脚手架,如here中所述现在我... 查看详情