Run a Background Task from a Controller Action in ASP.NET Core

Running background task on demand in asp.net core 3.x

I think the simplest approach is to make a fire-and-forget call in the code of handling the request to send a email, like this -

//all done, time to send email
Task.Run(async () =>
{
await emailService.EmailOffline(emailInfo).ConfigureAwait(false); //assume all necessary info to send email is saved in emailInfo
});

This will fire up a thread to send email.
The code will return immediately to the caller.
In your EmailOffline method, you can include time-delay logic as needed.
Make sure to include error logging logic in it also, otherwise exceptions from EmailOffline may be silently swallowed.

P.S. -
Answer to Coastpear and FlyingV -

No need to concern the end of calling context. The job will be done on a separate thread, which is totally independent of the calling context.

I have used similar mechanism in production for a couple of years, zero problem so far.

If your site is not supper busy, and the work is not critical, this is the easiest solution.
Just make sure you catch and log error inside your worker (EmailOffline, in this example).

If you need more reliable solution, I'd suggest using a mature queue product like AWS SQS, do not bother to create one by yourself. It is not an easy job to create a really good queue system.

Is it allowed to use Task.Run in an ASP.NET Core controller?

I know that this task could be lost due to application shutdown, but is it even allowed to offload work to the background using Task.Run in ASP.NET Core and never await the resulting task?

Oh, sure, nothing will prevent this. .NET and ASP.NET allow you to use Task.Run, and the response will be returned to the user without delay (assuming you ignore the task returned from Task.Run).

Is it okay to use Task.Run for the second operation and never await it and so return to the caller very soon?

You say that you're aware that the task could be lost in an app shutdown. And that's true; so it is entirely possible that the second deletion may never happen, and there will be no error or log message informing you of the fact. Furthermore, since the task is ignored, there will be no notification if that task fails, so if there's something wrong with the deletion code, no logs or anything will notify you of regular failures. If you're OK with that, then using fire-and-forget is fine.

On ASP.NET Core, you don't even need Task.Run; you can just call the (asynchronous) method that does the second delete operation and then ignore the task instead of awaiting it.

Simply ignoring tasks will work (with or without Task.Run), but this does have the negative side effect that ASP.NET isn't aware of the offloaded work at all. So I would recommend registering the task and having something delay the app shutdown until the tasks have a chance to complete. With registration, it is still possible to lose tasks, but registering them reduces the chance of losing tasks.

How do you launch a background process in ASPNET Core 3.0?

Since this is probably a follow-up from your previous question about IHostedService, I am going to assume that you want to have some background service (as a hosted service) within your ASP.NET Core application that is able to perform background tasks. And now you want to trigger such a task through a controller or Razor page action and have it executed in the background?

A common pattern for this is to have some central storage that keeps track of the tasks which both the background service and the web application can access. A simple way to do this is to make it a (thread-safe) singleton service that both sides can access.

The docs actually show a simple example using a BackgroundTaskQueue which is exactly that shared service/state. If you have a worker for a specific kind of job though, you could also implement it like this:

public class JobQueue<T>
{
private readonly ConcurrentQueue<T> _jobs = new ConcurrentQueue<T>();
private readonly SemaphoreSlim _signal = new SemaphoreSlim(0);

public void Enqueue(T job)
{
_jobs.Enqueue(job);
_signal.Release();
}

public async Task<T> DequeueAsync(CancellationToken cancellationToken = default)
{
await _signal.WaitAsync(cancellationToken);
_jobs.TryDequeue(out var job);
return job;
}
}

You can then register an implementation of this with the service collection along with a hosted background service that works on this queue:

services.AddSingleton<JobQueue<MyJob>>();
services.AddHostedService<MyJobBackgroundService>();

The implementation of that hosted service could then look like this:

public class MyJobBackgroundService : BackgroundService
{
private readonly ILogger<MyJobBackgroundService> _logger;
private readonly JobQueue<MyJob> _queue;

public MyJobBackgroundService(ILogger<MyJobBackgroundService> logger, JobQueue<MyJob> queue)
{
_logger = logger;
_queue = queue;
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var job = await _queue.DequeueAsync(stoppingToken);

// do stuff
_logger.LogInformation("Working on job {JobId}", job.Id);
await Task.Delay(2000);
}
}
}

In a controller action or a Razor page model, you then just need to inject the JobQueue<MyJob> and then call Enqueue on it to add a job to the list. Once the background service is ready to process it, it will then work on it.

Finally note that the queue is obviously in-memory, so if your application shuts down, the list of yet-to-do jobs is also gone. If you need, you could also persist this information within a database of course and set up the queue from the database.

asp.net core 2 web api task run background task

Your question far too broad, but generally speaking, you need some sort of background process and a way to schedule work on that. In that regard, there's literally a myriad of options, but they fall into one of these three main buckets:

  1. IHostedService in ASP.NET Core. There's sample code for a queue-based hosted service that would likely meet your needs. This is probably the simplest way to get going, but also the least robust. In particular, if your application stops, the queue will go with it, potentially resulting in lost items.

  2. A third-party tool like Hangfire. This is a good middle-of-the-road approach. Relatively easy to set up and relatively resilient.

  3. A message queue, like RabbitMQ. Essentially, you send a message to the queue, and then you have "subscribers" that listen on that queue for particular message types and take actions based on that. This is a little more complicated to set up, but also much more resilient than other approaches. There's ways to ensure message delivery, even when there's failures, and you can actually have multiple workers to round-robin the queue for scale.

How to run a background service on demand - not on application startup or on a timer

You need to look at a "queued background service" where you can submit "jobs" to it and it will perform those jobs in a background queue.

The work flow goes like this:

  1. Caller sends a request to the service with some parameters
  2. Service generates a "job" object and returns an ID immediately via 202 (accepted) response
  3. Service places this job in to a queue that is being maintained by a BackgroundService
  4. Caller can query the job status and get information about how much has been done and how much is left to go using this job ID
  5. Service finishes the job, puts the job in to a "completed" state and goes back to waiting on the queue to produce more jobs

Here is a very long-winded explanation on how it works: https://stackoverflow.com/a/63429262/1204153

Here is an example I made a while back: https://github.com/sonicmouse/ComputationService



Related Topics



Leave a reply



Submit