Domanda

Ho sempre pensato che aspettarmi che le eccezioni venissero lanciate su base regolare e usarle come logica di flusso fosse una cosa negativa. Le eccezioni sembrano essere, beh, la " eccezione " ;. Se ti aspetti e pianifichi un'eccezione, ciò sembrerebbe indicare che il tuo codice dovrebbe essere sottoposto a refactoring, almeno in .NET ...
Però. Uno scenario recente mi ha dato una pausa. Ho pubblicato questo su msdn qualche tempo fa, ma mi piacerebbe generare più discussioni al riguardo e questo è il posto perfetto!

Quindi, supponiamo che tu abbia una tabella di database che ha una chiave esterna per diverse altre tabelle (nel caso che originariamente ha provocato il dibattito, c'erano 4 chiavi esterne che lo indicano). Si desidera consentire all'utente di eliminare, ma solo se NON ci sono riferimenti a chiave esterna; NON vuoi eliminare a cascata.
Normalmente faccio solo un controllo per vedere se ci sono riferimenti e, se ci sono, informo l'utente invece di fare l'eliminazione. È molto facile e rilassante scrivere che in LINQ come tabelle correlate sono membri sull'oggetto, quindi Section.Projects e Section.Categories ed eccetera è bello scrivere con intellisense e tutti ...
Ma il fatto è che LINQ deve colpire potenzialmente tutte e 4 le tabelle per vedere se ci sono righe di risultati che puntano a quel record e colpire il database è ovviamente sempre un'operazione relativamente costosa.

Il responsabile di questo progetto mi ha chiesto di cambiarlo per catturare una SqlException con un codice di 547 (vincolo di chiave esterna) e gestirlo in questo modo.
Ero ...
resistente .

Ma in questo caso, è probabilmente molto più efficiente ingoiare il sovraccarico legato all'eccezione piuttosto che ingoiare i 4 risultati del tavolo ... Soprattutto dal momento che dobbiamo fare il controllo in ogni caso, ma nel caso in cui siamo risparmiati quando non ci sono bambini ...
Inoltre, il database dovrebbe davvero essere il responsabile della gestione dell'integrità referenziale, è il suo lavoro e lo fa bene ...
Quindi hanno vinto e l'ho cambiato.

Ad un certo punto mi sembra ancora sbagliato .

Cosa ne pensate di aspettarvi e gestire intenzionalmente le eccezioni? Va bene quando sembra che sarà più efficiente del controllo in anticipo? È più confuso per il prossimo sviluppatore guardare il tuo codice o meno confuso? È più sicuro, poiché il database potrebbe essere a conoscenza di nuovi vincoli di chiave esterna per i quali lo sviluppatore potrebbe non pensare di aggiungere un controllo per? O è una questione di prospettiva su cosa pensi esattamente sia la migliore pratica?

È stato utile?

Soluzione

Wow,

Prima di tutto, puoi per favore distillare un po 'la domanda, mentre è stato bello leggere una domanda ben ponderata e spiegata, che era molto da digerire.

La risposta breve è "sì", ma può dipendere.

  • Abbiamo alcune applicazioni in cui abbiamo un sacco di logica aziendale legata alle query SQL (non il mio progetto Gov!). Se è così che è strutturato, la gestione può essere difficile da convincere altrimenti poiché "funziona già".
  • In questa situazione, fa davvero un grosso problema? Dal momento che è ancora un viaggio attraverso il filo e ritorno. Il server fa molto prima di rendersi conto che non può continuare (ovvero se c'è una sequenza di transazioni che hanno luogo alla tua azione, allora cade a metà strada, perdendo tempo?).
  • Ha senso fare prima il controllo nell'interfaccia utente? Aiuta con la tua applicazione? Se offre un'esperienza utente migliore? (vale a dire ho visto casi in cui si passa attraverso diversi passaggi in una procedura guidata, si avvia, quindi cade, quando aveva tutte le informazioni necessarie per rientrare dopo il passaggio 1).
  • La concorrenza è un problema? È possibile che il record possa essere rimosso / modificato o qualsiasi altra cosa prima che abbia luogo il commit (come nel classico File.Exists boo-boo).

Secondo me:

Vorrei entrambi . Se riesco a fallire rapidamente e fornire un'esperienza utente migliore, fantastico. Eventuali eccezioni SQL (o altre) previste dovrebbero essere comunque catturate e restituite in modo appropriato.

So che esiste un consenso sul fatto che le eccezioni non debbano essere utilizzate se non per circostanze eccezionali , ma ricorda che qui stiamo attraversando i confini delle applicazioni, non aspettarti nulla . Come ho detto, questo è come il File.Exists , non ha senso, può essere eliminato prima di accedervi comunque.

Altri suggerimenti

Il tuo vantaggio è assolutamente giusto. Le eccezioni non sono solo per una volta in situazioni di luna blu, ma in particolare per la segnalazione di risultati diversi da quelli previsti.

In questo caso verrebbe comunque eseguito il controllo della chiave esterna e le eccezioni sono il meccanismo tramite il quale è possibile ricevere una notifica.

Quello che NON dovresti fare è catturare e sopprimere le eccezioni con un'istruzione catchall coperta. La gestione delle eccezioni a grana fine è in particolare il motivo per cui le eccezioni sono state progettate in primo luogo.

Penso che tu abbia ragione, le eccezioni dovrebbero essere usate solo per gestire i risultati imprevisti , qui stai usando un'eccezione per gestire un risultato probabilmente previsto, dovresti trattare questo caso in modo esplicito ma ancora prendere l'eccezione per mostrare un possibile errore.

A meno che questo non sia il modo in cui questo caso viene gestito in tutto il codice, mi schiererei con te. Il problema relativo alle prestazioni dovrebbe essere sollevato solo se si tratta effettivamente di un problema, ovvero dipenderebbe dalle dimensioni di tali tabelle e dal numero di volte in cui questa funzione viene utilizzata.

Bella domanda. Tuttavia, trovo le risposte ... spaventose!
Un'eccezione è una specie di GOTO.
Non mi piace usare le eccezioni in questo modo perché questo porta al codice spaghetti.
Semplice come quella.

Non c'è niente di sbagliato in quello che stai facendo. Le eccezioni non sono intrinsecamente "eccezionali". Esistono per consentire agli oggetti chiamanti di gestire in modo accurato gli errori in base alle loro esigenze.

Catturare la specifica SqlException è la cosa giusta da fare. Questo è il meccanismo mediante il quale SQL Server comunica la condizione della chiave esterna. Anche se potresti favorire un diverso utilizzo del meccanismo di eccezione, è così che fa SQL Server.

Inoltre, durante il controllo sulle quattro tabelle, alcuni altri utenti potrebbero aggiungere un record correlato prima del completamento del controllo, ma dopo aver letto quella tabella.

Consiglio di chiamare una procedura memorizzata che controlla se ci sono dipendenze, quindi cancella se non ce ne sono. In questo modo, viene eseguita la verifica dell'integrità.

Ovviamente, probabilmente vorresti una diversa procedura memorizzata per le eliminazioni singleton rispetto all'eliminazione batch ... L'eliminazione batch potrebbe cercare righe figlio e restituire un set di record che non erano idonei per un'eliminazione in blocco (aveva file secondarie).

Non mi piace vedere eccezioni ovunque in un programma, ma in questo caso utilizzerei il meccanismo delle eccezioni.

Anche se esegui un test in LINQ, dovrai intercettare l'eccezione nel caso in cui qualcuno inserisca un record figlio durante il test di integrità con LINQ. Dato che devi comunque verificare l'eccezione, perché duplicare il codice?

Un altro punto è che questo tipo di "corto raggio" l'eccezione non causa problemi di manutenzione, né rende più difficile la lettura del programma. Avrai il tentativo, la chiamata SQL da eliminare e il catch, tutto ciò entro 10 righe di codice, insieme. L'intenzione è ovvia.

Non è come lanciare un'eccezione che deve essere catturata 5 chiamate di procedura in alto nello stack, con il problema di essere sicuri che tutti gli oggetti in mezzo siano stati curati (liberati) correttamente.

Le eccezioni non sono sempre una risposta magica, ma non mi sentirei sbagliato a usarle in questa situazione.

I miei due centesimi,

Yves

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top