8

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.

TheQuertin
  • 81
  • 1
  • 1
  • 3
  • 7
    It's not really 'fire and forget' if you're wanting a return value later – p3tch Mar 16 '18 at 10:49
  • In that case I do not need that value - I just need to debug DoWork method when controller Action method returned other value. In different method there will be case where I need that value from DoWork – TheQuertin Mar 16 '18 at 10:56
  • 1
    Maybe this will answer your question: [Run a background task from a controller action in ASP.NET Core](https://stackoverflow.com/questions/49813628/run-a-background-task-from-a-controller-action-in-asp-net-core/49814520#49814520) – Fabio Jun 12 '22 at 05:34

4 Answers4

2

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();;
        });
Peter Kerr
  • 1,649
  • 22
  • 33
1

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

Alexei - check Codidact
  • 22,016
  • 16
  • 145
  • 164
  • 1
    From hangfire.MemoryStorage Readme.md: `It can be useful for testing purpose like check the behaviour and use it in a development environment. Please note that : it should not be used in production (no integrity and no thread safe even if many cases are managed).` – Dinh Tran Feb 10 '23 at 10:10
  • @DinhTran Yes, it is indeed useful just for testing purposes. – Alexei - check Codidact Feb 10 '23 at 13:26
0

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.

alsami
  • 8,996
  • 3
  • 25
  • 36
0

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));

Adem Catamak
  • 1,987
  • 2
  • 17
  • 25