使用ASP.Net Core中间件启动后台任务

6
我尝试在ASP.Net Core中加载页面时运行异步任务,即我希望在用户路由到页面时立即运行任务,但在任务完成之前显示页面。似乎在ASP.NET Core中,您可以使用中间件执行此类任务。因此,我尝试将以下内容添加到Startup.cs中。
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider serviceProvider)
        {

// Other configurations here
app.Use(async (context, next) =>
            {
                if (context.Request.Path.Value.Contains("PageWithAsyncTask"))
                {
                    var serviceWithAsyncTask = serviceProvider.GetService<IMyService>();
                    await serviceWithAsyncTask .DoAsync();
                }
                await next.Invoke();
            });

app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });

}

上述方法存在的问题是,由于我们在 DoAsync 完成之前不调用 next.Invoke(),因此页面加载会有延迟。我该如何正确实现上述方法,以便在启动 DoAsync 后立即调用 next.Invoke()
5个回答

16

6

自从 Asp.Net core 2.1 开始,使用后台任务实现 IHostedService 变得非常方便,只需从 BackgroundService 基类派生。以下是一个示例,取自 这里:

public class MyServiceA : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        Console.WriteLine("MyServiceA is starting.");

        stoppingToken.Register(() => Console.WriteLine("MyServiceA is stopping."));

        while (!stoppingToken.IsCancellationRequested)
        {
            Console.WriteLine("MyServiceA is doing background work.");

            await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
        }

        Console.WriteLine("MyServiceA background task is stopping.");
    }
}

然后,只需在 Startup.ConfigureServices 中进行注册:

services.AddSingleton<IHostedService, MyServiceA>();

正如Stephen Cleary指出的那样,Asp.Net应用程序可能不是后台任务的最佳地点(例如,在IIS中托管应用程序时,由于应用程序池的重启而被关闭),但对于某些情况,它可以很好地应用。


4

与其

await serviceWithAsyncTask .DoAsync();

你可以使用
 ThreadPool.QueueUserWorkItem(delegate {
       SomeMethod();
   });

在这种方法中,将使用来自线程池的额外线程,这当然是必要的,如果你想让代码在主线程之外的线程上运行 :-)。此块之后放置的任何代码都会立即运行。另请注意,如果您的Web服务器进程(Kestral)由IIS或其他反向代理重新启动,则您的后台工作者将立即中止。因此,您的后台工作者需要考虑到这一点进行防御性编写。还请注意,SomeMethod()本身不是async方法。但它是从后台线程调用的,因此它在异步运行(即独立于主线程)。

4
ASP.NET不适用于后台任务。我强烈建议使用适当的架构,例如Azure Functions / WebJobs / Worker Roles / Win32 services /等,并使用可靠的队列(如Azure queues / MSMQ /等)让ASP.NET应用程序与其服务进行通信。
然而,如果你真的想要-并且愿意承担风险(具体来说,你的工作可能会被中止),那么你可以使用IApplicationLifetime

这个范围之外,是关于使用Quartz.Net进行作业调度的Asp.Net Web API应用程序吗? - ibubi
@ibubi:Quartz.Net和HangFire各有其缺点;我认为它们是IApplicationLifetime的替代方案。 - Stephen Cleary
谢谢您的回复,但我不太明白。有数百个Asp.net&Quartz.Net或Hangfire实现教程都建议使用它们,您能否提供一个简要的文档,解释为什么我们应该避免在Asp.Net中使用这两个或自定义库? - ibubi
2
@ibubi 我从未使用过Quartz.NET。但至少对于Hangfire,您需要确保您的Web应用程序始终保持运行状态。现在它正在占用您的网站可能正在使用的资源,而且现在您无法将其与您的网站分开扩展。当然,这一切都假设您将Hangfire托管在您的Web应用程序项目中。我在将Hangfire作为单独的控制台应用程序托管时取得了巨大成功,我将其作为Windows服务运行,并使用TopShelf - 这消除了缺点,但意味着您需要一个单独的项目来部署。 - mason
1
@ibubi:我建议你自己提出问题。在回答评论中讨论这么多内容有些困难。 - Stephen Cleary

1

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