You have the following options:
IHostedServiceclasses 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 aSingletonclass with a ConcurrentQueue that you pass in to your controller, or something like anIDistributedCacheor more complex external pub/sub systems. Then you can just poll the queue in yourIHostedServiceand run certain operations based on it. Here is a microsoft example ofIHostedServiceimplementation for handling queues https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio#queued-background-tasks
Note that theSingletonclass approach can cause issues inmulti-serverenvironments.
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.
}
}
}
- Create a method that returns a
Task, pass in aIServiceProviderto 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
}
}
Update: Here is the Microsoft example of doing something similar: https://learn.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