Numero di riga errato sulla traccia dello stack

Ho questo codice

try { //AN EXCEPTION IS GENERATED HERE!!! } catch { SqlService.RollbackTransaction(); throw; } 

Il codice sopra è chiamato in questo codice

 try { //HERE IS CALLED THE METHOD THAT CONTAINS THE CODE ABOVE } catch (Exception ex) { HandleException(ex); }
try { //HERE IS CALLED THE METHOD THAT CONTAINS THE CODE ABOVE } catch (Exception ex) { HandleException(ex); } 

L’eccezione passata come parametro al metodo “HandleException” contiene il numero di riga della linea “throw” nella traccia dello stack anziché la riga reale in cui è stata generata l’eccezione. Qualcuno sa perché questo potrebbe accadere?

EDIT1 Ok, grazie a tutti per le vostre risposte. Ho cambiato il fermo interno per

 catch(Exception ex) { SqlService.RollbackTransaction(); throw new Exception("Enrollment error", ex); }
catch(Exception ex) { SqlService.RollbackTransaction(); throw new Exception("Enrollment error", ex); } 

Ora ho la linea corretta sulla traccia dello stack, ma ho dovuto creare una nuova eccezione. Speravo di trovare una soluzione migliore 🙁

    EDIT2 Forse (se hai 5 minuti) potresti provare questo scenario per verificare se ottieni lo stesso risultato, non molto complicato da ricreare.

    Sì, questa è una limitazione nella logica di gestione delle eccezioni. Se un metodo contiene più di una istruzione di lancio che genera un’eccezione, otterrai il numero di riga dell’ultimo che ha gettato. Questo codice di esempio riproduce questo comportamento:

     using System; class Program { static void Main(string[] args) { try { Test(); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } Console.ReadLine(); } static void Test() { try { throw new Exception(); // Line 15 } catch { throw; // Line 18 } } } 

    Produzione:

     System.Exception: Exception of type 'System.Exception' was thrown. at Program.Test() in ConsoleApplication1\Program.cs:line 18 at Program.Main(String[] args) in ConsoleApplication1\Program.cs:line 6 

    La soluzione è semplice, basta usare un metodo di supporto per eseguire il codice che potrebbe generare un’eccezione.

    Come questo:

     static void Test() { try { Test2(); // Line 15 } catch { throw; // Line 18 } } static void Test2() { throw new Exception(); // Line 22 } 

    Il motivo alla base di questo comportamento scomodo è che la gestione delle eccezioni .NET è basata sul supporto del sistema operativo per le eccezioni. Chiamato SEH, Structured Exception Handling in Windows. Che è basato sullo stack frame, può esserci solo un’eccezione triggers per ogni frame dello stack. Un metodo .NET ha uno stack frame, indipendentemente dal numero di blocchi di scope all’interno del metodo. Usando il metodo helper, ottieni automaticamente un altro stack frame in grado di tracciare la propria eccezione. Inoltre, il jitter sopprime automaticamente l’ottimizzazione dell’inlining quando un metodo contiene un’istruzione throw , quindi non è necessario utilizzare esplicitamente l’attributo [MethodImpl].

    “But throw: preserva la traccia dello stack !! usa throw;

    Quante volte hai sentito che … Beh, chiunque abbia programmato .NET per un po ‘lo ha quasi sicuramente sentito e probabilmente l’ha accettato come il tutto e finendo tutte le eccezioni’ rethrowing ‘.

    Sfortunatamente non è sempre vero. Come spiega @hans, se il codice che causa l’eccezione si verifica nello stesso metodo del throw; dichiarazione quindi la traccia dello stack viene ripristinata su quella linea.

    Una soluzione è estrarre il codice all’interno del try, catch in un metodo separato, e un’altra soluzione è lanciare una nuova eccezione con l’eccezione rilevata come eccezione interna. Un nuovo metodo è leggermente impacciato e una new Exception() perde il tipo di eccezione originale se si tenta di catturarlo più in alto nello stack di chiamate.

    Ho trovato una migliore descrizione di questo problema è stato trovato sul blog di Fabrice Marguerie .

    Ma ancora meglio c’è un’altra domanda StackOverflow che ha delle soluzioni (anche se alcune di esse implicano una riflessione):

    In C #, come posso ripristinare InnerException senza perdere traccia dello stack?

    La data / ora del tuo file .pdb corrisponde al tuo file .exe / .dll? In caso contrario, potrebbe essere che la compilazione non sia in “modalità di debug” che genera un nuovo file .pdb su ogni build. Il file pdb ha i numeri di riga precisi quando si verificano eccezioni.

    Controlla le impostazioni di compilazione per assicurarti che i dati di debug siano generati, o se ti trovi in ​​un ambiente di test / produzione, controlla il file .pdb per assicurarti che i timestamp corrispondano.

    A partire da .NET Framework 4.5 è ansible utilizzare la class ExceptionDispatchInfo per eseguire questa operazione senza la necessità di un altro metodo. Ad esempio, prendendo in prestito il codice dall’ottima risposta di Hans, quando usi solo il throw , in questo modo:

     using System; class Program { static void Main(string[] args) { try { Test(); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } Console.ReadLine(); } static void Test() { try { throw new ArgumentException(); // Line 15 } catch { throw; // Line 18 } } } 

    Emette questo:

     System.ArgumentException: Value does not fall within the expected range. at Program.Test() in Program.cs:line 18 at Program.Main(String[] args) in Program.cs:line 6 

    Ma puoi usare ExceptionDispatchInfo per catturare e rigettare l’eccezione, in questo modo:

     using System; class Program { static void Main(string[] args) { try { Test(); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } Console.ReadLine(); } static void Test() { try { throw new ArgumentException(); // Line 15 } catch(Exception ex) { ExceptionDispatchInfo.Capture(ex).Throw(); // Line 18 } } } 

    Quindi produrrà questo:

     System.ArgumentException: Value does not fall within the expected range. at Program.Test() in Program.cs:line 15 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at Program.Test() in Program.cs:line 18 at Program.Main(String[] args) in Program.cs:line 6 

    Come potete vedere, ExceptionDispatchInfo.Throw aggiunge ulteriori informazioni alla traccia dello stack dell’eccezione originale, aggiungendo il fatto che è stata nuovamente generata, ma mantiene il numero di riga e il tipo di eccezione originale. Vedere la documentazione MSDN per ulteriori informazioni.

    Le tracce dello stack C # vengono generate al momento del lancio, non al momento della creazione dell’eccezione.

    Questo è diverso da Java, dove le tracce dello stack sono riempite al momento della creazione dell’eccezione.

    Questo è apparentemente dal design.

    Lo capisco spesso nei sistemi di produzione se il Optimize code è selezionato. Questo azzanna i numeri di linea anche nel 2016.

    Assicurati che la configurazione sia impostata su “Rilascio” o su qualsiasi configurazione tu stia creando e distribuendo sotto. La casella di controllo ha un valore diverso per configurazione

    In definitiva, non so mai quanto sia più “ottimizzato” il mio codice con questo controllo, quindi ricontrollalo se necessario, ma in molte occasioni ho salvato la traccia dello stack.

    inserisci la descrizione dell'immagine qui