Come gestire le eccezioni che vengono assorbite da una libreria di terze parti?

5

Attualmente sto riscontrando un problema con un fornitore di librerie di controllo di terze parti. Hanno un'eccezionale cultura occulte che ostacola il mio approccio generale di fail-fast nello sviluppo del software.

Un esempio: il loro controllo di rete propone un evento RowValidating in cui è possibile verificare l'input dei dati dell'utente prima che venga eseguito il commit sull'oggetto sottostante. L'argomento dell'evento ha una proprietà booleana IsValid e una proprietà stringa ErrorText . Se il gestore ha la proprietà IsValid impostata su False , la griglia considera la riga non valida, presenta il testo dell'errore seguito da "Vuoi correggere il valore?" in una finestra di dialogo sì / no.

Il problema è che se il gestore lancia un Exception effettivo durante il processo di convalida, la griglia lo cattura e si comporterà esattamente come descritto sopra - solo utilizzerà la proprietà Message dell'eccezione rilevata come testo di errore di convalida. L'istanza di eccezione, anche se in realtà è il mio codice che l'ha provocata, non può mai essere catturata in seguito. Non c'è modo per me di gestirlo a parte il wrapping del codice di convalida in un blocco try / catch che cattura tutto.

Ovviamente non voglio che il mio codice generi eccezioni, ma hey ... errare humanum est ! Se ciò dovesse accadere, preferirei molto far sì che l'app si arresti in modo anomalo e brucino piuttosto che nascondere l'eccezione ! Per questi scenari ho un gestore globale che registrerà l'eccezione non gestita appena prima che l'app si arresti in modo anomalo. Questo è dove mi aspetterei che qualcuno di loro finisse.

Dato che la mia azienda ha fatto un investimento enorme in questo particolare fornitore di terze parti (sia dall'apprendimento che dal codice reale in esecuzione con esso), non posso semplicemente correre verso un altro fornitore, tanto meno il mio controllo della rete.

Ho provato a parlare con il venditore in modo che questo venga risolto, ma non faranno nulla per timore di causare cambiamenti improvvisi per altri clienti (comprensibili). Inoltre, non introdurranno alcun tipo di bandiera booleana per eludere il comportamento (meno comprensibile).

In questo momento mi sento a disagio nel distribuire un'app in cui so che è possibile che venga eseguita in uno stato danneggiato. Odio anche dover avvolgere il mio codice evento in un blocco try / catch che potrebbe incontrare un Exception da cui è impossibile recuperare con garbo.

Che tipo di soluzione posso usare per prevenire o risolvere questo problema?

TL; DR : il fornitore di terze parti toglie% istanze diException generate dal mio codice, soluzioni alternative sono brutte e insoddisfacenti, non sanno cosa fare con questo.

    
posta Crono 10.02.2015 - 18:47
fonte

5 risposte

2

Recentemente ho risolto un problema simile con una libreria di terze parti. Permettetemi di ripetere per assicurarmi di non interpretare male la vostra situazione. Sai come aggirare il problema, ma non ti piace la ripetizione della soluzione alternativa e ritieni che offuschi il tuo codice effettivo?

Ho risolto il problema utilizzando un decoratore python che rileva un'eccezione e lo gestisce in modo appropriato. In questo modo, è solo un addizionale @handle_errors alle mie definizioni di funzione, non un ingombrante blocco try-catch che viene ripetuto ovunque. Non ho molta familiarità con la linea di prodotti .net, ma so che ci sono librerie di programmazione orientate all'aspetto che puoi ottenere, oppure puoi usare pattern di decorazione per ottenere una riduzione simile nel boilerplate.

Sfortunatamente, non c'è molto oltre a quello che puoi fare. È solo il prezzo del venditore a bloccarsi. Tuttavia, questa sembra essere una bassa probabilità, una situazione a basso impatto, con routine di validazione che dovrebbero essere per lo più senza stato e relativamente facili da testare a fondo. Inoltre, le eccezioni sono difficilmente "nascoste" se vengono presentate all'utente. Questa non è la situazione descritta nel blog di Jeff Atwood.

    
risposta data 10.02.2015 - 20:58
fonte
5

Poiché i controlli dell'interfaccia utente standard di .NET framework non rilevano le eccezioni non gestite da soli e offrono alcuni meccanismi per rilevare tali eccezioni in un punto centrale, sono d'accordo sul fatto che sia discutibile il comportamento di un controllo di terze parti.

Supponiamo che, dalla natura della tua applicazione, in caso di grave fallimento, sei sicuro al 100% di poter uscire in sicurezza dall'applicazione in qualsiasi momento, anche in un evento GUI arbitrario, senza il rischio di lasciarti dietro troppo dati incoerenti. Quindi potresti prendere in considerazione la creazione di un wrapper funzionale come questo:

public class MyExit
{
  public void WhenUnhandledException(Action action)
  {
    try
    {
        action();  
    }
    catch(Exception ex)
    {
         // ... do some additional logging, if you like ...
         Environment.FailFast(ex.Message+"\n"+ex.StackTrace);
    }
  }
}

e usa questa funzione per tutto il tuo codice in questo modo:

  void MyGUIExceptionHandler(object o, EventArgs e)
  {
     MyExit.WhenUnhandledException(() => {
       // add code, maybe using o and e
     });
  }

In questo modo, non è necessario ripetere lo stesso codice try / catch / log / FailFast più di una volta e, se lo desideri, puoi modificare la strategia fail-fast e logging.

    
risposta data 10.02.2015 - 23:31
fonte
2

Che cosa c'è da fare. A mio avviso, la decisione di trasformare un'eccezione durante la convalida in una convalida fallita è un modo corretto di gestire tali eccezioni.

  • Il superamento dell'eccezione e l'arresto anomalo dell'applicazione comporta il rischio significativo di perdere il lavoro che l'utente ha svolto, anche se la situazione è stata causata da input utente errati e completamente correggibili.
  • In una routine di validazione, è necessario controllare solo se l'input è valido e non apportare modifiche allo stato dell'applicazione. Pertanto, se un'eccezione viene lanciata in una routine di convalida, ci dovrebbe essere il rischio zero che lo stato dell'applicazione sia corrotto.
    Se stai facendo più di una semplice convalida nel tuo gestore, allora è un tuo problema assicurarti che non vi sia alcun percorso con uno stato dell'applicazione non valido.
risposta data 10.02.2015 - 20:06
fonte
2

Sono molto affezionato a hard error & fallire velocemente, credo che siano l'unica strada giusta da percorrere, ma cerco di non essere dogmatico per loro.

Ci sono casi in cui la cosa migliore da fare con un'eccezione inaspettata è registrarla e ingoiarla. Ti darò un esempio più semplice della tua situazione: supponi di avere una collezione osservabile che, una volta modificata, invia una notifica a un elenco di osservatori registrati. E supponiamo che uno degli osservatori lanci un'eccezione. È colpa della collezione? No, in quanto non ha alcun controllo su chi si registra con esso. È colpa del codice che ha modificato la collezione? Certamente no, quel codice probabilmente non sapeva nemmeno che la collezione che stava modificando era osservabile. E 'colpa di qualcuno degli osservatori rimasti che sono registrati e si aspettano anche di ricevere le loro notifiche? Inoltre no, poiché ognuno di essi non ha conoscenza di altri osservatori e non ha alcun controllo su chi verrà prima informato. Quindi, chiaramente, l'unica cosa che ha senso per la raccolta osservabile da fare è registrare l'eccezione, inghiottirla e continuare a invocare gli osservatori rimanenti nell'elenco come se nulla fosse accaduto.

Quindi, il creatore di quella libreria si trova in una situazione simile e ha dovuto prendere la stessa decisione.

Quello che vorrei fare nei tuoi panni è che racchiudere tutti i miei punti di ingresso con try-catch per essere sicuro che un messaggio di errore significativo venga sempre restituito alla libreria che mi chiama.

Sarebbe bello avere il supporto del linguaggio per fare cose del genere, (decoratori generati automaticamente che catturano eccezioni e invocano un overridable per gestirli) e la risposta di Karl Bielefeldt sembra indicare che Python ha un tale meccanismo, ma quelli di noi che non hanno simili sottigliezze sono bloccati a farlo a mano.

EDIT:

Il tuo bisogno come sviluppatore di sapere che si è verificata un'eccezione non fatale dovrebbe essere per lo più coperto registrando l'eccezione. (Stai guardando il tuo registro mentre esegui il debug dell'applicazione, vero?)

Ora, se, come me, vuoi eliminare la possibilità che qualsiasi eccezione non fatale scorrendo dal log e passi inosservata, puoi fare quello che faccio:

Ho un metodo di utilità centralizzato per ingoiare le eccezioni che, subito dopo aver registrato l'eccezione, esegue la seguente chiamata estremamente utile:

System.Diagnostics.Debugger.Break();

Sul campo, questo non fa nulla. Ma in un ambiente di sviluppo, (mentre è in esecuzione con il debugger), questo irrompe nel debugger, che teoricamente mi consente di esaminare lo stato della macchina e ottenere maggiori informazioni sulla causa dell'eccezione, ma ancora più importante, assicura che che ho notato che l'eccezione è stata lanciata.

Naturalmente questo presuppone che durante lo sviluppo della tua applicazione non lo si esegua mai, si esegue sempre il debug. Questa è la modalità operativa corretta. Se per caso non hai già questa abitudine, ti consiglio vivamente di acquistarla.

    
risposta data 10.02.2015 - 22:04
fonte
-1

Quindi, qui scrivi il gestore che genera l'eccezione e la griglia gestisce l'eccezione stessa.

Lo ritengo abbastanza giusto - dopo tutto, stai scrivendo il codice della libreria che getta sugli stati di errore, e il chiamante (cioè la griglia) intercetta e gestisce l'eccezione come preferisce, in questo caso mostra un messaggio a l'utente in una finestra di dialogo (che è perfettamente comprensibile dato il suo controllo dell'interfaccia utente).

Ora capisco che ti piacerebbe che alcune eccezioni passassero e bloccassero l'app, o ti permettessero di catturarle, e avrebbero potuto solo rilevare eccezioni basate sulla loro stessa gerarchia invece di catturare tutte le eccezioni, ma hanno fatto quella scelta. Dovrai solo vivere con non essere in grado di catturare le eccezioni e gestirle all'interno del gestore (possibilmente impostando un po 'di stato altrove o inviando un messaggio alternativo per notificare la tua applicazione).

    
risposta data 11.02.2015 - 10:29
fonte

Leggi altre domande sui tag