2010-02-22 11 views
18

Sta avendo un tentativo di intercettazione/intercettazione un segnale che non stai codificando in modo pulito? Mi chiedo perché nella mia cattura sto chiamando un altro metodo e se fallisce ottengo un altro errore di runtime quindi sono tentato di avvolgere quelle chiamate nella cattura con un altro tentativo/cattura di nuovo. Mi chiedo se è normale farlo?Prova/Cattura nidificata

ad es.

catch (Exception ex) 
    { 
     transaction.VoidOrder(transactionID); 

     LogError(ex.ToString()); 
     Response.Redirect("Checkout", false); 
    } 

in modo che il VoidOrder o anche i metodi LogError potrebbero bombardare fuori. In questo momento quando chiamo VoidOrder, ottengo un riferimento null su transactionID perché chiama un metodo BL e in quel metodo BL sto rilanciato in modo da poterlo rilevare a questo livello superiore nel codice sopra. Ma se sto gettando di nuovo dentro un fermo, allora ho bisogno di prenderlo anche io.

+0

Si spera che questo sia solo uno pseudo-codice, ma probabilmente non dovresti prendere nemmeno un'eccezione generica. –

+2

Va bene catturare un'eccezione generica come un catch-all, purché tu abbia tentato di rilevare prima eccezioni specifiche (dato che puoi avere più rilevazioni per prova) ... Il solo sopra si dovrebbe essere più specifico ... –

+0

Per quello che vale, vorrei prima registrare l'errore. Se Transaction.voidorder ha un'eccezione, non hai record di –

risposta

15

Ecco come ci avviciniamo al problema:

Tutte le chiamate dal livello di interfaccia utente/codebehind ad altri livelli utilizzare un try-catch, dove abbiamo sempre prendere un eccezione personalizzata. Tutte le azioni eseguite dai livelli sottostanti hanno il loro try-catch, che registra, avvolge e lancia l'eccezione personalizzata. L'interfaccia utente può quindi fare affidamento su questo e cercare le eccezioni gestite con messaggi di errore amichevoli.

Codebehind:

protected void btnSubmit_Click(object sender, EventArgs e) 
{ 
    //do something when a button is clicked... 
    try 
    { 
     MyBL.TakeAction() 
    } 
    catch(MyApplicationCustomException ex) 
    { 
     //display something to the user, etc. 
     ltlErrorPane.Text = ex.Message; 

     //or redirect if desired 
     if(ex.ErrorType == MyCustomErrorsType.Transactional) 
     { 
      Response.Redirect("~/Errors/Transaction.aspx"); 
     } 
    } 
} 

BL:

Nel livello di business, alcuna operazione che possa fallire l'uso di un try-catch, che registra e avvolge la questione prima di gettarla al UI.

public class MyBL 
{ 
    public static void TakeAction() 
    { 
     try 
     { 
      //do something 
     } 
     catch(SpecificDotNetException ex) 
     { 
      //log, wrap and throw 
      MyExceptionManagement.LogException(ex) 
      throw new MyApplicationCustomException(ex, "Some friendly error message", MyCustomErrorsType.Transactional); 
     } 
     finally 
     { 
      //clean up... 
     } 
    } 
} 

gestore di eccezioni:

Il gestore di eccezioni attuale ha molteplici modi per registrare, tra cui registro eventi, registro di file ed e-mail, infine, se tutto il resto fallisce. Scegliamo di restituire semplicemente false se il logger non può eseguire nessuna delle azioni previste. IMO questa è una scelta personale però. Riteniamo che la probabilità di 3 metodi falliti in successione (il log degli eventi fallisce, prova il log del file, fallisce, prova la posta elettronica, fallisce) è molto improbabile. In questo caso abbiamo scelto di consentire all'app di continuare. L'altra opzione sarebbe quella di consentire all'app di fallire completamente.

public static class MyExceptionManagement 
{ 
    public static bool LogException(Exception ex) 
    { 
     try 
     { 
      //try logging to a log source by priority, 
      //if it fails with all sources, return false as a last resort 
      //we choose not to let logging issues interfere with user experience 

      //if logging worked 
      return true; 
     } 
     catch(Exception ex) 
     { 
      //in most cases, using try-catch as a true-false is bad practice 
      //but when logging an exception causes an exception itself, we do 
      //use this as a well-considered choice. 
      return false; 
     } 
    } 
} 

Infine, come una cassetta di sicurezza fail-, noi non implementare il gestore di eventi globale Application_Error (in Global.asax). Questa è l'ultima risorsa per i casi in cui non abbiamo provato correttamente a catturare qualcosa. Generalmente effettuiamo il log e il reindirizzamento a una pagina di errore amichevole. Se la gestione degli errori personalizzati di cui sopra viene eseguita correttamente, pochissimi errori lo faranno al gestore globale.

Spero che questo possa aiutare un po '. È una soluzione possibile. Ha funzionato molto bene per noi per diversi anni su alcune applicazioni più grandi.

+0

L'OP sta chiedendo come gestire lo scenario in cui un comando nel blocco catch genera un'eccezione, ad es. // log prova a scrivere su un file di log su un file system che non c'è più –

+0

Grazie. Modificato per chiarezza anche su questo problema .. –

+0

sì, ma cosa succede se non si desidera il BL da buttare e si vuole dire di reindirizzare l'utente alla cassa su eventuali errori maggiori o minori. Se si genera un errore esplicito nel BL, non è possibile reindirli a meno che non si passi indietro dal BL. Quindi puoi usare un try/catch nel BL, ma devi lanciare e lasciare che il code-behind catturi e gestisca elegantemente. – PositiveGuy

0

Hai pensato di utilizzare l'oggetto TransactionScope invece di provare a eseguire il rollback della transazione manualmente? So che a volte questo è impossibile, ma il più delle volte per me TransactionScope ha funzionato. In questo modo, non dovevo gestire manualmente i rollback: i roll-back manuali potrebbero essere comunque un problema, perché i dati sono gentili in uno stato "instabile", che potrebbe rovinare la tua intera logica.

+1

Non è necessario eseguire il rollback. Sto inviando chiamate SOAP. In caso di esito negativo, reindirizzare l'utente alla cassa. – PositiveGuy

+0

Scusa, ho capito il tuo VoidOrder come una specie di rollback, dal momento che devi occuparti di alcuni dati transazionali in memoria. Rispondendo alla tua domanda originale, sembra che potrebbe essere necessario inserire un codice più protettivo nel BL. Come hai sottolineato, oggi ricevi un'eccezione null ref, che potresti dover soddisfare. Arriverete a un punto in cui solo gli errori che ottenete dall'applicazione sono errori reali imprevisti dai quali non potete recuperare, e quindi utilizzerei il modello di rethrow suggerito sopra. –

5

I blocchi nidificati try/catch sono inevitabili nelle applicazioni di livello superiore che devono registrare, inviare messaggi o reagire alle eccezioni in altri modi non banali.

Ci sono modi per ridurre il numero di blocchi annidati (ad esempio, gestione degli errori consolidamento utilizzando HttpApplication.Error gestore di ASP.NET (aka Application_Error)), ma si dovrebbe prendere eventuali eccezioni prodotte dal vostro codice di gestione e implementare un piano di backup nel caso in cui tutto il resto fallisce.

1

Un'altra soluzione al problema di nidificazione sarebbe incapsulare la logica try/catch per le funzioni interne (LogError, ecc.) In quelle funzioni, invece di dipendere dal chiamante per catturarle. Per LogError, questo avrebbe senso perché probabilmente si vorrà gestire un errore nel registro degli errori nello stesso modo, indipendentemente da chi sta tentando di registrare un errore.

1

Forse è una domanda se qualcuno può capire perché stai usando il try/catch annidato. A volte diventa inevitabile usarli, ma io dico che di solito non è carino.
Si dovrebbe considerare che la separazione delle preoccupazioni è un must.

Un metodo getControl non deve inserire uno script in esso, né un metodo di salvataggio dovrebbe fare di più che salvare.

Esaminare cosa dovrebbero fare esattamente questi metodi e loro avranno la risposta. Non sembra così imbarazzante annullare un ordine in caso di errore. Sembra ragionevole.