await Task.WhenAll(tasks) Exception Handling, log all exceptions from the tasks

I am trying to figure out how to report all exceptions thrown by a list of tasks from the code below.

The basic idea of this code snippet is: The user sends a request to the handler, the handler creates the messages tasks and send them to a class that send them to the external system. I included the methods involved below.

I must have something off because I was debugging the exception handler and the tasks Exception are always null because it seems their status is Waiting for Activiation unless I stay in the breakpoint long enough.

I was using this link Task Continuations will not Handle your Exceptions as a reference to get started.

// Handle the user request
public async void Handle(WriteScanToSys settings)
{
  _successfulScanIds = new List<int>();

  // create the messages as tasks
  var tasks = _factory.CreateMessage(settings).Select(msg => SendScans(msg));

  try
  {
    // wait for all of them to complete
    await Task.WhenAll(tasks); // I had ConfigureAwait(false) here, but took it off
  }
  catch (Exception)
  {
    foreach (var task in tasks.Where(t => t.Exception != null))
    {
      // ELMAH
      var errorLog = ErrorLog.GetDefault(null);
      errorLog.Log(new Error(task.Exception));
    }
  }

  // save to repository
}

// the task to perform
private async Task<IDictionary<string, object>> SendScans(IDictionary<string, object> data)
{
  object sysMsg = null;
  var response = await _msgCenter.SendMessage(data);
  response.TryGetValue("SystemMessage", out sysMsg);
  _successfulScanIds.Add(Convert.ToInt32(data["Id"]));
  return response;
}

// the communication with the external system (The message center class)
private async Task<IDictionary<string, object>> SendMessage(IDictionary<string, object> msg)
{
  var response = new Dictionary<string, object>();

  var response = await _client.sendAsync(
                          new BodyOfRequest(
                              // Compose Object
                          ));

  if (response.ScreenMessage != "SUCCESSFUL")
    throw new CustomException("The transaction for job " + job + " failed with msg: " + body.ScreenMessage);

  response.Add("SystemMessage", body.ScreenMessage);

  return response;
}
Jon Skeet
people
quotationmark

You've fallen foul of lazy evaluation - the result of Select will create a new set of tasks each time you iterate over it. You can fix this just by calling ToList():

var tasks = _factory.CreateMessage(settings)
                    .Select(msg => SendScans(msg))
                    .ToList();

That way the set of tasks that you're awaiting will be the same set of tasks checked with your foreach loop.

people

See more on this question at Stackoverflow