从.NET Core中的控制器运行单个后台任务的最简单方法是什么? [英] What is the simplest way to run a single background task from a controller in .NET Core?

查看:58
本文介绍了从.NET Core中的控制器运行单个后台任务的最简单方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有WebAPI控制器的ASP.NET Core Web应用程序.我想做的就是,在某些控制器中,能够启动将在后台运行的进程,但是控制器应继续执行并在该进程完成之前返回.我不希望服务的使用者必须等待这项工作完成.

I have an ASP.NET Core web app, with WebAPI controllers. All I am trying to do is, in some of the controllers, be able to kick off a process that would run in the background, but the controller should go ahead and return before that process is done. I don't want the consumers of the service to have to wait for this job to finish.

我看过有关IHostedService和BackgroundService的所有帖子,但似乎都不是我想要的.另外,所有这些示例都向您展示了如何设置事物,但没有实际调用它的方式,或者我不了解其中的一些内容.

I have seen all of the posts about IHostedService and BackgroundService, but none of them seem to be what I want. Also, all these examples show you how to set things up, but not how to actually call it, or I am not understanding some of it.

我尝试了这些,但是当您在Startup中注册IHostedService时,它将在该时间点立即运行.这不是我想要的.我不想在启动时运行任务,我希望能够在需要时从控制器调用它.另外,我可能有几种不同的方式,所以仅注册services.AddHostedService()将不起作用,因为我可能拥有MyServiceB和MyServiceC,因此如何从控制器中获得正确的权限(我不能只是注入IHostedService)?

I tried these, but when you register an IHostedService in Startup, it runs immediately at that point in time. This is not what I want. I don't want to run the task at startup, I want to be able to call it from a controller when it needs to. Also, I may have several different ones, so just registering services.AddHostedService() won't work because I might have a MyServiceB and MyServiceC, so how do I get the right one from the controller (I can't just inject IHostedService)?

最终,我所看到的一切都是巨大的,令人费解的代码混乱,看起来似乎应该是一件如此简单的事情.我想念什么?

Ultimately, everything I have seen has been a huge, convoluted mess of code for something that seems like it should be such a simple thing to do. What am I missing?

推荐答案

您有以下选择:

  1. IHostedService 类可以是长时间运行的方法,这些方法在应用程序的整个生命周期中都在后台运行.为了使他们能够处理某种后台任务,您需要实现某种全局"操作.您的应用程序中的队列系统,供控制器存储数据/事件.该队列系统可以像具有Singleton 类一样简单.传入控制器的-1?view = netcore-3.1"rel =" nofollow noreferrer> ConcurrentQueue 或类似 IDistributedCache 或更复杂的外部发布/订阅系统的东西.然后,您可以在 IHostedService 中轮询队列并基于该队列运行某些操作.这是 IHostedService 实现的Microsoft示例,用于处理队列
  1. IHostedService classes can be long running methods that run in the background for the lifetime of your app. In order to make them to handle some sort of background task, you need to implement some sort of "global" queue system in your app for the controllers to store the data/events. This queue system can be as simple as a Singleton class with a ConcurrentQueue that you pass in to your controller, or something like an IDistributedCache or more complex external pub/sub systems. Then you can just poll the queue in your IHostedService and run certain operations based on it. Here is a microsoft example of IHostedService implementation for handling queues https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio#queued-background-tasks Note that the Singleton class approach can cause issues in multi-server environments. Example implementation of the Singleton approach can be like:

// Needs to be registered as a Singleton in your Startup.cs
public class BackgroundJobs {
  public ConcurrentQueue<string> BackgroundTasks {get; set;} = new ConcurrentQueue<string>();
}

public class MyController : ControllerBase{
  private readonly BackgroundJobs _backgroundJobs;
  public MyController(BackgroundJobs backgroundJobs) {
    _backgroundJobs = backgroundJobs;
  }

  public async Task<ActionResult> FireAndForgetEndPoint(){
    _backgroundJobs.BackgroundTasks.Enqueue("SomeJobIdentifier");
  }
}

public class MyBackgroundService : IHostedService {
  private readonly BackgroundJobs _backgroundJobs;
  public MyBackgroundService(BackgroundJobs backgroundJobs)
  {
    _backgroundJobs = backgroundJobs;
  }

  public void StartAsync(CancellationToken ct)
  {
    while(!ct.IsCancellationRequested)
    {
      if(_backgroundJobs.BackgroundTasks.TryDequeue(out var jobId))
      {
        // Code to do long running operation
      }
    Task.Delay(TimeSpan.FromSeconds(1)); // You really don't want an infinite loop here without having any sort of delays.
    }
  }
}

  1. 创建一个返回 Task 的方法,将 IServiceProvider 传递给该方法,并在其中创建新的Scope,以确保ASP.NET不会终止该任务控制器操作完成时.像
  1. Create a method that returns a Task, pass in a IServiceProvider to that method and create a new Scope in there to make sure ASP.NET would not kill the task when the controller Action completes. Something like

IServiceProvider _serviceProvider;

public async Task<ActionResult> FireAndForgetEndPoint()
{
  // Do stuff
  _ = FireAndForgetOperation(_serviceProvider);
  Return Ok();
}

public async Task FireAndForgetOperation(IServiceProvider serviceProvider)
{
  using (var scope = _serviceProvider.CreateScope()){
    await Task.Delay(1000);
    //... Long running tasks
  }
}

更新:这是Microsoft进行类似操作的示例:

Update: Here is the Microsoft example of doing something similar: https://docs.microsoft.com/en-us/aspnet/core/performance/performance-best-practices?view=aspnetcore-3.1#do-not-capture-services-injected-into-the-controllers-on-background-threads

这篇关于从.NET Core中的控制器运行单个后台任务的最简单方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆