1

I have the following action in my controller:

[HttpPost("run")]
public Task<object> Run([FromBody] ResearchRequest researchRequest)
{
   researchService.RunAsync(researchRequest);
   return new{ queued = true   };
}

The controller needs to handle a task that takes several minutes.
Is this the correct way to release researchService.RunAsync to handle its job?

Or is there a better approach.

Thanks

Jamal Salman
  • 199
  • 1
  • 10
fatnjazzy
  • 6,070
  • 12
  • 57
  • 83
  • Generally speaking, this is a correct approach. However, you should also think about some other things. E.g. If someone posts many times the same `ResearchRequest` how should this be handled ? Wouldn't make sense to check if the researchRequest has already been queued, instead of queueing one more ? Furthermore, I don't see the reason why you need the `async` here. If understand correct, that you want is a fire-and-forget action, you don't need the controller to wait for the completion of the process of the submitted `researchRequest`. Correct ? – Christos Jan 07 '20 at 11:28
  • 5
    You've flagged your method `async` but you're not `await`ing anything. – Sean Jan 07 '20 at 11:32
  • @Christos thank you. (the async is there by mistake). how would i check if there is a task already running (I need some data context from te queued task to make sure it is running)? thanks. – fatnjazzy Jan 07 '20 at 11:58
  • @fatnjazzy You can add a flag in the researchService and set it to true when you enter RunAsync and false when you are about to return, and then you do a quick check in the Run action method before executing RunAsync – Jamal Salman Jan 07 '20 at 12:11
  • Related: [How should I run a long-running task in ASP.NET?](https://stackoverflow.com/questions/5553001/how-should-i-perform-a-long-running-task-in-asp-net-4) Short answer: You shouldn't. Offload long-running logic to a separate service. – John Wu Jan 07 '20 at 23:14

2 Answers2

0

If you are wanting to check that a process is already running, you could mark it as in progress somewhere on the server side (in a task in the database or such) and then when displaying the UI call a method on the server to check the state of your in progress flag.

that way the UI could navigate away from that page and return to still see that the process had been started

Ant Perry
  • 1
  • 3
0

You can do that if RunAsync is making I/O requests, but you wouldn't return a Task:

[HttpPost("run")]
public object Run([FromBody] ResearchRequest researchRequest)
{
   researchService.RunAsync(researchRequest);
   return new{ queued = true   };
}

That will start running RunAsync on the same thread, just like any other method. At the first await in RunAsync that acts on an incomplete Task, RunAsync will return its own incomplete Task, at which point control returns back to your Run action and your object is returned. You won't be waiting for whatever I/O operation RunAsync makes.

If RunAsync is taking a long time because of CPU calculations (not I/O), then that won't do anything for you because, remember, it starts running on the same thread. You will have to start it on another thread, which you can do using Task.Run:

[HttpPost("run")]
public Task<object> Run([FromBody] ResearchRequest researchRequest)
{
   Task.Run(() => researchService.RunAsync(researchRequest));
   return new{ queued = true   };
}

But!

In both cases, ASP.NET will have no idea that RunAsync is running in the background. If the IIS app pool is shut down or recycled for any reason, that job will be killed part way through. Note that by default, IIS is configured to shut down an app pool after 20 minutes of no HTTP requests coming in.

If that is unacceptable to you, then you're better off writing the job to a queue in a database or something and doing that background processing in a Windows service.

Gabriel Luci
  • 38,328
  • 4
  • 55
  • 84