2012-12-15 10 views
9

So che l'attesa non può essere utilizzata nella clausola di cattura.attendono proibito nella clausola di cattura. Alla ricerca di un posto di lavoro

Fondamentalmente ho un livello che è responsabile per la ricezione di richieste in arrivo, l'elaborazione, la costruzione di messaggi da loro e passare i messaggi a un altro livello responsabile di inviare i messaggi.

Se qualcosa va storto durante l'invio del messaggio, viene generata un'eccezione personalizzata e viene rilevata dal livello di invio del messaggio. A questo punto, un record di errore per questo messaggio dovrebbe essere inserito nel DB (richiede un po 'di tempo, quindi asincrono), e l'eccezione dovrebbe essere propagata al livello superiore che ha il compito di inviare una risposta di errore al client che ha effettuato la richiesta .

Ecco il codice molto semplificata per scopo illustrativo qui sotto:

public async Task ProcessRequestAsync(Request req) 
{ 
    int statusCode = 0; 

    try 
    {  
     await SendMessageAsync(new Message(req));   
    } 
    catch(MyCustomException ex) 
    { 
     statusCode = ex.ErrorCode; 
    } 

    await SendReponseToClientAsync(req, statusCode); 
} 


public async Task SendMessageAsync(Message msg) 
{ 
    try 
    {   
     // Some async operations done here 
    } 
    catch (MyCustomException ex) 
    {  
     await InsertFailureForMessageInDbAsync(msg, ex.ErrorCode); // CAN'T DO THAT 
     throw; 
    } 
} 

Naturalmente il livello di richiesta di elaborazione non sa nulla del DB, è solo responsabile della costruzione di un messaggio, passando il messaggio al messaggio livello di elaborazione e inviare una risposta al cliente (positivo o negativo).

Quindi penso che abbia senso ... Se si verifica un'eccezione, il mio livello "business" desidera inserire un record di errore nel DB e riscrivere l'eccezione in modo che il livello di elaborazione "richiesta" possa fare ciò che è necessario (in questo contesto, inviare una risposta negativa al cliente).

Le eccezioni non devono essere utilizzate in questo modo? Mi sembra pulito, ma il fatto che non riesca a farlo nella clausola catch mi fa pensare che ci sia forse un odore di codice con il design (anche se ho l'idea di gestire l'eccezione in un layer per poi ripensarlo per il il livello superiore per fare un po 'di manipolazione e suona anche a me come se fosse esattamente ciò che le eccezioni sono fatte per).

Qualche idea intorno a questo?

Grazie!

+1

Sembra che sia possibile rilevare eccezioni nel livello di invio e restituire semplicemente un oggetto di stato al chiamante. Si potrebbe anche creare un campo di questo stato oggetto di tipo "Eccezione" in modo che possa essere nuovamente lanciato in seguito, se necessario. – dlev

+1

Perché non chiami semplicemente la funzione e non ti aspetti? – Rafael

+0

@dlev: Grazie per l'idea, ancora non mi sento molto gentile a mixare lo stato di ritorno in qualche posto e usare l'eccezione in un altro posto. È necessario scegliere tra il codice di stato di restituzione (o oggetto) o l'eccezione di lancio, ma non il mixaggio di entrambi (questo è ciò che ricordo dalle linee guida di progettazione "buone" per segnalare errori). – darkey

risposta

6

Ho incontrato anche questo un paio di volte.

Come ha commentato Rafael, si può semplicemente ignorare il risultato di InsertFailureForMessageInDbAsync:

public async Task SendMessageAsync(Message msg) 
{ 
    try 
    {   
    // Some async operations done here 
    } 
    catch (MyCustomException ex) 
    {  
    var _ = InsertFailureForMessageInDbAsync(msg, ex.ErrorCode); 
    throw; 
    } 
} 

Si noti che eventuali eccezioni InsertFailureForMessageInDbAsync verranno ignorati per impostazione predefinita.

L'altra opzione è più complessa:

public async Task DoSendMessageAsync(Message msg) 
{ 
    // Some async operations done here 
} 

public async Task SendMessageAsync(Message msg) 
{ 
    var task = DoSendMessageAsync(msg); 
    MyCustomException exception = null; 
    try 
    { 
    await task; 
    return; 
    } 
    catch (MyCustomException ex) 
    { 
    exception = ex; 
    } 

    await Task.WhenAll(task, InsertFailureForMessageInDbAsync(msg, exception.ErrorCode)); 
} 

Questo modo asincrono gestire l'eccezione e restituire un Task che ha un vero e proprio AggregateExption (contenente sia le eccezioni se InsertFailureForMessageInDbAsync non gettare uno).

Sfortunatamente, await ignorerà la seconda eccezione. Se si vuole veramente tutte le eccezioni passati, è possibile sostituire l'ultima riga (await Task.WhenAll...) con qualcosa di simile:

Exception exception2 = null; 
try 
{ 
    await InsertFailureForMessageInDbAsync(msg, exception.ErrorCode); 
} 
catch (Exception ex) 
{ 
    exception2 = ex; 
} 

if (exception2 == null) 
    throw new AggregateException(exception); 
else 
    throw new AggregateException(exception, exception2); 

ma questo è piuttosto complessa, e non esattamente il tipo di modello che si desidera ripetere. Se possibile, ignorerei il risultato del logging come raccomandato da Rafael.

+0

Bello Stephen! Direi che speravo in un modo molto più facile per andare in giro così non sono andato così lontano come hai fatto nella riflessione. Ma dopotutto, non c'è un modo semplice per gestirlo;) Grazie mille per la tua risposta comunque, penso che andrò semplicemente nel modo più semplice, ma almeno saprò come fare il "duro" se davvero necessario. – darkey

Problemi correlati