简体   繁体   中英

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.

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.

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

Quite frankly, this looks like an XY problem . Performing a fire and forget call in a controller doesn't seem to make much sense. If you know your operation will take some time, it probably would be a better idea to use a queue and a dequeuer webjob.

That being said, what you want to achieve is possible: your DoWork method simply has to be async .

public IActionResult Action(int num)
{
    //operations-1
    Task.Run(() => DoWork(num)); // async call not awaited

    //operations-2
    int a = num + 123;
    return Ok(a);
}

public async Task<bool> DoWork(int num)
{
    await Task.Delay(10000);
    var i = 9; // breakpoint will hit after 10s
    return true;
}

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM