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

查看:30
本文介绍了从 .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 类可以是长时间运行的方法,在您的应用程序生命周期内在后台运行.为了让它们处理某种后台任务,您需要实现某种全局"您的应用程序中的队列系统供控制器存储数据/事件.这个队列系统可以像带有 ConcurrentQueue,您传递给您的控制器,或者类似 IDistributedCache 或更复杂的外部发布/订阅系统.然后你可以在你的 IHostedService 中轮询队列并基于它运行某些操作.这是用于处理队列的 IHostedService 实现的微软示例 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio#queued-background-tasks请注意,Singleton 类方法可能会在 multi-server 环境中导致问题.单例方法的示例实现可以是这样的:
  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 执行类似操作的示例:https://docs.microsoft.com/en-us/aspnet/core/performance/性能最佳实践?view=aspnetcore-3.1#do-not-capture-services-injected-into-the-controllers-on-background-threads

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天全站免登陆