'C# ASP.NET Core Fire and forget
I have a sync controller method
public IActionResult Action(int num)
{
//operations-1
Task.Run(() => DoWork(num));
//operations-2
int a = num + 123;
return Ok(a);
}
and DoWork method
private bool DoWork(int num)
{
//operations
return true;
}
What I'm trying to do is to run DoWork method in background when calling that endpoint from Postman, but I want to get result in Postman and then debug DoWork method (from breakpoint in DoWork method) - is it possible?
For that moment, controller action and DoWork() are executing simultaneously but when I reach
return Ok(a);
applications waits for DoWork instead of returning value. I have tried also
Task.Factory.StartNew(() => DoWork());
ThreadPool.QueueUserWorkItem(o => DoWork());
but result is the same.
I want DoWork method to return value but that value is not neccessary by controller action method - it will be used in different place, not connected with that.
Solution 1:[1]
Tasks are high level threads that make sure you are not blocking any context.
You either want to use something like RabbitMQ or IHostedService from ASP.NET Core 2.0 to do fire and forget task that kick in after a request has completed.
Solution 2:[2]
If you use Db in project, you can use Hangfire It is easy to use background process manager. https://www.hangfire.io/
you can use it very easyly like BackgroundJob.Enqueue(() => DoWork(num));
Solution 3:[3]
Use a background queue sometimes is overkill. There are a number of sites showing a way to do when you need to access the database context. The problem of Task.Run in a controller, is that the spawned task cannot access the same context as the controller uses as it may (probably will) get disposed before that Task accesses it. You can get round this by ensuring that the sub task only references Dependencies it knows will stay alive, either by using a singleton service or better for database context, using the IServiceScopeFactory .
The crux of this is to create a seperate dependency that can handle your database context or Repository. This can be injected into your controller as normal.
public void Execute(Func<IRepository, Task> databaseWork)
{
// Fire off the task, but don't await the result
Task.Run(async () =>
{
// Exceptions must be caught
try
{
using var scope = _serviceScopeFactory.CreateScope();
var repository = scope.ServiceProvider.GetRequiredService<IRepository>();
await databaseWork(repository);
}
catch (Exception e)
{
Console.WriteLine(e);
}
});
}
Then call this from your controller such as
// Delegate the slow task another task on the threadpool
_fireForgetRepositoryHandler.Execute(async repository =>
{
// Will receive its own scoped repository on the executing task
await repository.DoLOngRunningTaskAsync();;
});
Solution 4:[4]
Note: This builds upon Adem Catamak's answer.
Hangfire can be used, but no actual database is required because it can work with memory storage:
services.AddHangfire(opt => opt.UseMemoryStorage());
JobStorage.Current = new MemoryStorage();
While it has some overhead, Hangfire allows managing these jobs as opposed to having stuff running async and requiring custom code for simple things like run time, unhandled exceptions, custom code for DI support.
Credit: Codidact
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | alsami |
| Solution 2 | Adem Catamak |
| Solution 3 | |
| Solution 4 | Alexei - check Codidact |
