2015-03-02 8 views
5

Sto cercando di capire come segnalare tutte le eccezioni generate da un elenco di attività dal codice sottostante.attende Task.WhenAll (tasks) Gestione eccezioni, registra tutte le eccezioni dalle attività

L'idea di base di questo frammento di codice è: L'utente invia una richiesta al gestore, il gestore crea i messaggi tasks e li invia a una classe che li invia al sistema esterno. Ho incluso i metodi coinvolti di seguito.

Devo avere qualcosa in sospeso perché stavo eseguendo il debug del gestore di eccezioni e le attività Eccezione sono sempre nulle perché sembra che il loro stato sia Waiting for Activiation a meno che non rimanga nel breakpoint abbastanza a lungo.

Stavo usando questo collegamento Task Continuations will not Handle your Exceptions come riferimento per iniziare. fallo

// 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; 
} 

risposta

7

Sei caduto di valutazione pigra - il risultato di Select creerà un nuovo insieme di attività ogni volta che scorrere su di esso. È possibile risolvere questo problema semplicemente chiamando ToList():

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

In questo modo l'insieme di attività che si sta aspetti la sarà lo stesso insieme di attività sono stati controllati con la vostra foreach loop.

+0

sai ho pensato che, ma saltato fuori al momento per una ragione io non posso davvero pensare al momento. Avrei dovuto saperlo specialmente quando il debugger non è stato in grado di valutare le attività durante il tentativo di eseguirne il drill. A volte penso che servano gli occhi di qualcun altro. Grazie a @JonSkeet –

1

Invece di iterare su tutte le attività, è possibile ottenere le eccezioni (se presenti) dal Task.WhenAll -Task:

var taskResult = Task.WhenAll(tasks); 
try 
{ 
    await taskResult; 
} 
catch (Exception e) 
{ 
    if (taskResult.IsCanceled) 
    { 
     // Cancellation is most likely due to a shared cancellation token. Handle as needed, possibly check if ((TaskCanceledException)e).CancellationToken == token etc.  
    } 
    else if (taskResult.IsFaulted) 
    { 
     // use taskResult.Exception which is an AggregateException - which you can iterate over (it's a tree! .Flatten() might help) 
     // caught exception is only the first observed exception 
    } 
    else 
    { 
     // Well, this should not really happen because it would mean: Exception thrown, not faulted nor cancelled but completed 
    } 
}