8

Sto lavorando a un Web Hook in .NET 4.0 che eseguirà un lambda in modo asincrono e quindi pubblicheremo il risultato su un dato URI quando è finito.Voglio che l'attività gestisca eventuali eccezioni generate, ma trovo difficile impedire loro di raggiungere il genitore

Ho il diritto di funzionare, ma ora voglio che l'attività gestisca tutte le eccezioni generate e trovo difficile impedire loro di raggiungere il genitore.

Ecco parte del mio codice:

private readonly Func<T> _startTask; 
private readonly string _responseUri; 

public Task<T> Begin() 
{ 
    var task = new Task<T>(_startTask); 
    task.ContinueWith<T>(End); 
    task.Start(); 
    return task; 
} 

private T End(Task<T> task) 
{ 
    if (task.IsFaulted) 
    { 
     return HandleException(task); 
    } 

    var result = task.Result; 
    WebHookResponse.Respond(result, _responseUri); 
    return result; 
} 

private T HandleException(Task<T> task) 
{ 
    WebHookResponse.HandleException(task.Exception.InnerException, _responseUri); 
    return null; 
} 

Una versione alternativa che ho provato chiamate ContinueWith() due volte per registrare un proseguimento per eseguire OnlyOnRanToCompletion e uno per l'esecuzione OnlyOnFaulted. (Non sono sicuro se chiamate ContinueWith() due volte è corretto.):

public Task<T> Begin() 
{ 
    var task = new Task<T>(_startTask); 
    task.ContinueWith<T>(End, TaskContinuationOptions.OnlyOnRanToCompletion); 
    task.ContinueWith<T>(HandleException, TaskContinuationOptions.OnlyOnFaulted); 
    task.Start(); 
    return task; 
} 

private T End(Task<T> task) 
{ 
    var result = task.Result; 
    WebHookResponse.Respond(result, _responseUri); 
    return result; 
} 

private T HandleException(Task<T> task) 
{ 
    WebHookResponse.HandleException(task.Exception.InnerException, _responseUri); 
    return null; 
} 

Quindi, fondamentalmente voglio un modo per ogni attività gestire le proprie eccezioni tramite una funzione di continuazione. Così com'è, la funzione di continuazione HandlException non viene mai chiamata in nessuno dei precedenti esempi.

Sto causando le eccezioni in un caso di test, e devo menzionare che sto usando una chiamata Tasks.WaitAll(tasks); su una serie di attività per assicurarmi che tutte le attività siano complete prima di dare le mie asserzioni, e non sono sicuro se quella chiamata fa la differenza su come le eccezioni sono gestite dalle attività. Attualmente WaitAll genera un'eccezione Aggregation che aggrega le eccezioni per ciascuna delle attività perché non vengono gestite dalla funzione di continuazione HandleException.

+0

Non vedo il problema. Che comportamento stai vedendo? –

+0

Siamo spiacenti, la funzione di continuazione HandleException non viene chiamata. An AggregationException viene lanciata da Tasks.WaitAll (tasks); chiamata. Non voglio vedere un AggregationException. Ho appena aggiornato la domanda. –

+0

@Martin Owen Ciao, il mio HandleExceptionContinuation non viene chiamato troppo e il mio evento UnobservedTaskException non viene licenziato anche qui: http://stackoverflow.com/questions/11831844/unobservedtaskexception-being-throw-but-it-is-handled-by- a-taskscheduler-unobser hai trovato qualche soluzione? – newway

risposta

1

Forse, per la risposta di Henk Holterman, l'ordine fa la differenza. Cioè,

var task = new Task<T>(_startTask); 
task = task.ContinueWith<T>(HandleException, TaskContinuationOptions.OnlyOnFaulted); 
task = task.ContinueWith<T>(End, TaskContinuationOptions.OnlyOnRanToCompletion); 
task.Start(); 

assicurerebbe che HandleException venga eseguito quando necessario.

+0

No, non è così. Entrambe le continuazioni verranno chiamate se necessario, indipendentemente dall'ordine in cui hai chiamato 'ContinueWith' (ovviamente solo una volta ogni volta che le loro' TaskContinuationOptions' si escludono a vicenda). –

+0

Se l'operazione è stata eseguita in questo ordine, le eccezioni aggregate in End non verranno gestite. HandleException dovrebbe sempre essere l'ultimo chiamato, come lo sweeper nel calcio è dietro a tutta la difesa, quindi può spazzare via tutto ciò che può passare, stessa cosa. – Despertar

3

Una continuazione dell'attività che osserva l'eccezione dell'attività non gestisce l'eccezione. Succede ancora ovunque tu aspetti per completare l'attività.

Hai detto che chiamavi WaitAll (attività) prima di affermare. Scommetto che la tua continuazione sarebbe stata eseguita se avessi dato abbastanza tempo, ma l'eccezione su WaitAll() di solito si verificherà prima dell'esecuzione della tua continuazione. Quindi le tue affermazioni sono probabilmente fallite prima che alla tua continuazione sia stata data la possibilità di finire il suo lavoro.

1

Io uso questo approccio perché fornisce uno stile di codifica fluente dichiarativo e non sporca il codice con gli aspetti di gestione delle eccezioni.

class Program 
{ 
    static void Main() 
    { 
     Task.Factory.StartNew(
      () => 
       { 
        throw new Exception(); 
       }) 
      .Success(() => Console.WriteLine("Works")) 
      .Fail((ex) => Console.WriteLine("Fails")).Wait(); 

     Console.ReadKey(); 
    } 
} 

public static class TaskExtensions 
{ 
    public static Task Success(this Task task, Action onSuccess) 
    { 
     task.ContinueWith((t) => 
     { 
      if (!t.IsFaulted) 
      { 
       onSuccess(); 
      } 
     }); 

     return task; 
    } 

    public static Task Fail(this Task task, Action<Exception> onFail) 
    { 
     return task.ContinueWith(
      (t) => 
      { 
       if (t.IsFaulted) 
       { 
        t.Exception.Handle(x => true); 
        onFail(t.Exception.Flatten()); 
       } 
      }); 
    } 
}