2015-11-24 4 views
12

Ho un metodo simile:Log catturato eccezioni di fuori del metodo in cui sono stati catturati

public TResult DoSomethingWithLogging<TResult>(Func<TResult> someAction) 
{ 
    try 
    { 
     return someAction.Invoke(); 
    } 
    catch (Exception ex) 
    { 
     LogException(ex) 
     throw; 
    } 

Questo metodo viene utilizzato come segue:

var result = DoSomethingWithLogging(() => Foo()); 

voglio anche registrare le eccezioni che sono state catturato all'interno di Foo(). Non riesco a utilizzare throw in catch all'interno di Foo.

Come posso rilevare tali eccezioni?

Esempio:

public static string Foo() 
{ 
    try 
    { 
     return "Foo"; 
    } 
    catch (Exception) 
    { 
     // I have to log this exception too without adding anything to Foo 
     return "Exception caught";    
    }  
} 
+6

Puoi spiegare perché hai questi requisiti? Sembrano molto specifici ed effettivamente paralizzano il modo in cui puoi scrivere un buon codice. Forse possiamo trovare una soluzione migliore adatta alle reali esigenze senza dover mutilare il codice per adattarlo? – Rob

+0

È nell'app MVC. Viene utilizzato nel controller come registrazione dell'importazione dei dati dal file csv.In caso di importazione fallita, dovrei memorizzare informazioni sul problema. Ma ora, quando si verificano determinati problemi, posso semplicemente saltare questa riga nel file. Ma devo ancora loggare perché alcune linee vengono saltate. –

+0

Continuo a pensare che te ne vai nel modo sbagliato. Ad esempio, potresti esporre un evento all'interno di foo che ti consente di rispondere alle eccezioni ignorate. Comunque dall'esterno presupponendo che qualcosa dall'interno di foo sia un cattivo design – Batavia

risposta

17

È possibile eseguire il binding all'evento FirstChanceException. Ecco il tuo codice modificato per dimostrare questo:

using System; 
using System.Runtime.ExceptionServices; 

public class Program 
{ 
    public static void Main() 
    { 
     AppDomain.CurrentDomain.FirstChanceException += 
     (object source, FirstChanceExceptionEventArgs e) => 
     { 
     Console.WriteLine("FirstChanceException event raised in {0}: {1}", 
      AppDomain.CurrentDomain.FriendlyName, e.Exception.Message); 
     }; 
    Console.WriteLine("Hello World"); 
    Console.WriteLine(DoSomethingWithLogging(() => Foo())); 
    } 

    public static TResult DoSomethingWithLogging<TResult>(Func<TResult> someAction) 
    { 
    try 
    { 
     return someAction.Invoke(); 
    } 
    catch (Exception ex) 
    { 
     Console.WriteLine(ex.Message); 
     throw; 
    } 
    } 

    public static string Foo() 
    { 
    try 
    { 
     throw new Exception("This will be caught"); 
     return"Foo"; 
    } 
    catch (Exception) //I have to log this exception too without adding anything too Foo 
    { 
     return "Exception caught";  
    }  
    } 
} 

Come regola, sarei molto cauto di questo in qualcosa di diverso debug scenari. Una volta catturato, non dovrebbe essere considerato un'eccezione dal codice più in alto. (Naturalmente, coglierlo in primo luogo avrebbe potuto essere un errore logico, quindi questo ha davvero un certo valore nel debug degli scenari).

Ci sono anche complicazioni nei casi multi-thread. Il codice sopra mostra come funziona FirstChanceException, ma se è stato allegato prima della chiamata e quindi staccato dopo essere stato ancora attivato da eventuali eccezioni su altri thread. Filtrare quelli fuori potrebbe essere difficile. Probabilmente comincerei considerando di guardare lo stack delle chiamate, ma non sono sicuro che sia il modo migliore.

2

Penso che lo state facendo nel modo sbagliato. È una pessima idea assumere qualcosa sull'implementazione di foo. (Come JDM citato nei commenti si tratta di un odore di codice e si otterrà nei guai)

Un modo per ridisegnare il vostro approccio è quello di utilizzare gli eventi

class FooClass { 

    public event SkippedWorkEventHandler SkippedWork; 

    public void Foo() { 
     try { 
     /* some code */ 
     } catch (Exception ex) { 
     if (SkippedWork != null) { 
      /* pass in the relevant data to eventargs */ 
      SkippedWork(this, EventArgs.Empty) 
     } 
     } 
    } 

    public void DoSomethingWithFoo() { 
    SkippedWork += new LogSkippedWorkEventHandler 

    try { 
     Foo() 
    } catch (Exception ex) { 
     /*handle uncaughed exceptions here */ 
    } 

    } 
} 

La ragione di questo è che da l'i esterno non garantirò che io uso try/catch per rilevare cosa succede all'interno di foo. potrei riscriverlo a un semplice se controllo e restituire se il mio test fallisce. Il risultato di foo è lo stesso eccetto che ora le tue eccezioni non vengono registrate.

Con l'esposizione di un evento si consente a chiunque dall'esterno che è interessato a determinate cose (una linea saltata in base al commento) di reagire. e hai fatto un contratto dicendo che onorerai/innalzerai questo evento.

+0

Intersting idea, ma sono sicuro che verrà rilevata un'eccezione, perché è un'app MVC con filtro OnException. E anche io non voglio modificare Foo, perché Foo non deve dipendere da un metodo wrapper, è un metodo dalla logica aziendale che viene chiamato dal controller MVC. E un altro ancora: ho dozzina di diversi Foo's –

+2

Il mio punto è che si crea una dipendenza se si fa affidamento su FirstChangeException. Vale a dire la dipendenza da Foo a FirstChangeException. Inoltre, chiunque voglia mai refactare foo per qualsiasi motivo avrà un MOLTO difficile momento di capire che alcuni codici vengono eseguiti mentre l'eccezione sembra essere completamente presa e gestita – Batavia

+1

Inoltre, direi che l'OP usa le eccezioni per gestire la logica di business è un odore di codice serio. Un'eccezione è qualcosa come "il file che hai provato a leggere non esiste" o "quella stringa non è un int". La logica aziendale come "questa riga del CSV contiene un valore che non è un codice postale valido" è un errore _business_, non un'eccezione. – JMD