Task cancelled exception even though the method call(from one API to another API) is awaited

Viewed 205

My requirement is to asynchronously process images for each request and once all the requests' images are processed, they are to be zipped. So for that i need to wait till all the images are processed.

Using a for loop for each item in my list to asynchronously call the method of the second API. However i need to wait for all the calls to the 2nd APIs method to execute before moving on to the next line in the code.

1st API:

public async void SegregateRecordsByRequestAndMerge(List<ClassB> listCDF, List<ClassA> records, string filePath)
{    
List<ClassA> RequestList = records.GroupBy(x => x.RequestId)
  .Select(g => g.First())
  .ToList();
Task[] taskList = new Task[RequestList.Count];
for (int i = 0; i < RequestList.Count; i++) // reqList in RequestList)
{
  ClassA reqList = new ClassA();
  reqList = RequestList[i];
  List < ClassA> recordsByRequest = records.Where(x => x.RequestId == reqList.RequestId).ToList();
  ModelA serviceInput = new ModelA();
  serviceInput.ClassBProperty = listCDF;
  serviceInput.ClassAProperty = recordsByRequest;
  taskList[i] = (Task.Factory.StartNew(() => ProcessImages(serviceInput, "myroute/processimage")));
}
Task.WaitAll(taskList, 600000);
}

This is how the service call is made

public void ProcessImages(CodingRecordAndCDFModel myServiceInput, string resource)
{
  var client = new ServiceClient(BaseUrl)
                   {
                    Resource = resource
                   };
  client.Post(JsonConvert.SerializeObject(myServiceInput).ToString());
}

2nd API(Image Processor) :

[HttpPost, Route("myroute/processimage"), ResponseType(typeof(string))]
public void GeneratePDFs([FromBody] string pdfServiceInput) {
  if (!ModelState.IsValid) {
    throw new Exception();
  }
  ModelA myServiceModel = new ModelA();
  myServiceModel  = JsonConvert.DeserializeObject <ModelA> (pdfServiceInput);
  //// Logic to Process images
}

The problem i am facing is that, even though i am using WaitAll() a task cancelled exception is thrown. The control comes back to the 1st API before the method in the 2nd API is even completely executed.

I tried multiple ways, like using Parallel.ForEach(), Task.Whenall(), Task.Delay, TaskCreationOptions.LongRunning but none of them work... all of them throw the exception that the task got cancelled.

I need the control to wait till all the calls to the 2nd API return back to the 1st API and then do some other logic.

Can someone suggest what should be done? or any better way to handle this requirement?

Edit 1:

I think the problem is that i am trying to call a method asychronously, which will create 2 different async method calls... and since i am using the await or waitall on each async method, the moment the 1st call's execution is done the control returns to the 1st API even though there is another thread running in the 2nd API.

If there is a way to await a for loop or to wait till all the individual async calls are returned then that would be the soulution.

1 Answers

According to David Fowl (Microsoft Architect) use of async void in ASP.NET Core applications is ALWAYS bad. Also be careful, depending on how many images you have to process, you could flood the thread pool if it's loads and loads..

How about this pattern, as long as you have access to update the service layer. Note: I've excluded some of your business logic for brevity..

public async Task SegregateRecordsByRequestAndMerge()
{
    List<ClassA> requestList = records.GroupBy(x => x.RequestId).Select(g => g.First()).ToList();
    var processTasks = new List<Task>();
    for (var i = 0; i < requestList.Count; i++)
    {
        processTasks.Add(ProcessImages(serviceInput, "myroute/processimage"));
    }

    await Task.WhenAll(processTasks);
}

public Task ProcessImages(CodingRecordAndCDFModel myServiceInput, string resource)
{
  var client = new ServiceClient(BaseUrl) { Resource = resource };
  await client.PostAsync(JsonConvert.SerializeObject(myServiceInput).ToString());
}
Related