Newsgroup: it.comp.dotnet
Titolo: try-catch miglior pratica

Una applicazione fa un classico uso di un DB server remoto.  Vorrei che 
tutti i colloqui col DB fossero 'protetti', ovviamente per avvisare 
l'operatore e non mandare in crash l'applicazione perche una qualsiasi 
query non ha avuto possibilità di esecuzione.
Qual'è secondo voi il miglior modo di non lasciare questo tipo di falle 
in una applicazione?
Proteggere dei macro-metodi con un bel try/catch sulla chiamata, anche 
se la funzione magari fa parecchie cose e non siamo in grado di reagire 
ad una eccezione che ci viene restituita? Oppure intervenire piu 
granularmente, e parare ogni accesso al DB?
O fare uso di unhandled exception a livello applicazione, così 'pariamo 
tutto' anche se la possibilità di reagire è pari a 0...
Personalmente impiego molto tempo nello sviluppo per questo aspetto, sul 
quale tra l'altro, tendo a tornare in un secondo tempo visto che di 
primo acchitto si tende ad andare avanti per vedere i risultati della 
logica che implementiamo, e stare nei tempi che ci vengono richiesti.
Apprezzerei molto un consiglio generale, mollte grazie.
Alessandro

 logica che implementiamo, e stare nei tempi che ci vengono richiesti.
Spesso poco tempo significa scarsa qualità. :-)
Scherzi a parte, se il tuo manager ti "impone" uno scarso controllo 
sulla qualità del software (niente test, niente log, niente tracciamento 
delle eccezioni) e poi in produzione fate una figura pessima, la colpa è 
solo sua. E' vero che tu, con la tua "etica da sviluppatore" (TM) 
dovresti farglielo notare, ma capisco anche che tante volte è difficile...
HTH,
Giulio
-- 

 logica che implementiamo, e stare nei tempi che ci vengono richiesti.
Assumendo che la strategia di accesso ai dati sia abbastanza uniforme
nel tempo, questo aspetto si puo' via via astrarre e diventare parte
integrante del codice di libreria.
-LV

 Spesso poco tempo significa scarsa qualità. :-)
Troppo spesso e' vero! In quella equazione va anche considerato lo
*scope*.
-LV

 possibilità di esecuzione.
Una strategia che paga molto ed è "pulita" è quella di usare le 
Action<T> con lambda.
Es:
public bool ExecuteActionOnDB(Action<SqlConnection> connectionAction)
{
 try
 {
  if(connectionAction == null)
   throw new ArgumentNullException(connectionAction);
  using(var cnn = new SqlConnection(...)
  {
   cnn.Open(); // close fatta dalla using
   connectionAction(cnn);
  }
 }
 catch(Exception err)
 {
  Log(err);
  return false;
 }
}
A questo punto hai centralizzato il punto in cui crei e usi la 
connection.
Per usarla ovunque fai così:
bool res = ExecuteActionOnDB(cnn => {
  var myCommand = GetCommand(...);
  myCommand.Connection = cnn;
  myCommand.Execute...
});
if(!res) ==> avvisi l'utente che qualcosa è andato storto
if(res)  ==> presenti i dati
ovviamente nel comunicare che c'è stato un errore non devi mai dare i 
dettagli dell'eccezione ma solo loggarti e spedirti un'email.
-- 
Raffaele Rialdi  
http://www.iamraf.net
Weblog: 
http://blogs.ugidotnet.org/raffaele
Microsoft MVP profile 
https://mvp.support.microsoft.com/profile/raffaele
UGIdotNET - 
http://www.ugidotnet.org/

 dettagli dell'eccezione ma solo loggarti e spedirti un'email.
Perché?
Ciao,
Giulio
-- 

 Perchï¿œ?
Perchï¿œ gli errori possono contenere dettagli che forniscono importanti 
spunti per l'hacker di turno.
ï¿œ uno dei punti di forza del google hacking:
http://en.wikipedia.org/wiki/Google_hacking
Questi simpatici ragazzi stanno tenendo un database degli errori di cui 
andare a caccia, in quanto rivelano informazioni che possono essere 
utilizzate per fare hacking:
http://www.exploit-db.com/google-dorks/7/
Esempio di un "Divide by zero":
http://www.exploit-db.com/ghdb/1339/
E dico simpatici in senso positivo perchï¿œ la comunitï¿œ di hacker ï¿œ 
l'unica che contribuisce a 360 gradi a mettere alla luce i problemi e a 
renderci consapevoli (per chi vuole ascoltare, ovviamente).
E btw ï¿œ anche una delle raccomandazioni della SDL (Security Development 
Lifecycle) che ï¿œ apprezzato anche al di fuori del mondo MS.


 spunti per l'hacker di turno.
[cut]
Perfetto.
Grazie,
Giulio
-- 

 connection.
Si', ma cio' che hai guadagnato e' un gestore del tutto generico, che
al limite si presta a gestire le eccezioni che nel mio post iniziale
metto sotto il punto 2. In piu', in un'applicazione reale, l'accesso
al log come minimo dovrebbe essere iniettato, per cui torniamo al
fatto che le eccezioni generiche andrebbero piuttosto gestite ad alto
livello (il catch della generica Exception e' infatti una bad practice
se non e' fatto top level). -- Ovviamente, sullo scheletro che
presenti si puo' costruire anche altro, e persino tutt'altro, ma cosi'
come lo presenti mi sembra che sia piu' fuorviante che altro.
-LV

 come lo presenti mi sembra che sia piu' fuorviante che altro.
Gli esempi servono a rendere l'idea, non a fare cut&paste in codice di 
produzione.


 produzione.
Certo: e -IMO- il tuo esempio (nonche' il discorso che ne e' seguito)
rende appunto un'idea...  sbagliata! :)
-LV

 rende appunto un'idea...  sbagliata! :)
Considerato che:
- la propagazione del vero motivo l'eccezione fino alla UI ï¿œ da 
scartare per motivi di sicurezza
- il logging ce lo puoi mettere come ti pare
Quel codice ti para da questi problemi che sono generici per natura.
Totalmente diverso ï¿œ l'errore dovuto ad una logica come il mapping o la 
concorrenza che *non* deve essere propagato in quanto tale per motivi 
di sicurezza ed anche perchï¿œ potrebbero verificarsi anche in totale 
assenza di un'eccezione di basso livello.
Perciï¿œ, che si tratti di una dbconcurrencyexception o di un errore 
nello strato di logica, questi sono errori di natura totalmente 
differenti che devono essere generati come scelta architetturale e che 
nulla hanno a che fare con le eccezioni come la sqlexception.
In questi casi infatti si usano application exception (re-throw se 
generate da un'eccezione di piï¿œ basso livello o generate direttamente 
dal nostro codice).


 nulla hanno a che fare con le eccezioni come la sqlexception.
Si': avevo fatto una serie di considerazioni di base, su certe
differenze fondamentali, nel mio primo post in questo thread. Magari
avresti potuto espandere a partire da quello, per maggiore precisione
nonche' per una collaborazione piu' fruttuosa... ma questa e' solo una
mia personale opinione.
-LV

 mia personale opinione.
La collaborazione è possibile se il tono è disteso, cosa che tu non 
concedi quasi mai nei tuoi post.
Se cambiassi atteggiamento sarebbe certamente possibile.
Per contro il mio post iniziale non rappresentava affatto uno scontro 
con quanto da te affermato precedentemente.
Poi però te le vai a cercare ...


 concedi quasi mai nei tuoi post.
Te lo concedo: ma e' che a me occorre una base di
onesta' (intellettuale) su cui distendersi.
-LV

 connessioni.
Scusate se interrompo una discussione tra persone sicuramente 
preparatissime, ma tornando al mero codice mi soffermo su questo punto 
sopra, che seguivo anch'io utilizzando il precendente e versatilissimo 
ado.net
 (sqlConnection, sqlCommand..etc). E' che non riesco ancora a 
realizzarlo utilizzando EF. Se ad esempio data una entity ( una tabella) 
voglio indietro un oggetto corrispondente ad un ID, come posso rendere 
questo piccolo metodo universale,  cioe valido per N tabelle diverse? 
Cosa metto nella linq select?
Grazie.....Alessandro


Con EF4 hai un context che implementa IDisposable quindi puoi costruire 
un pattern analogo dove hai la using del context all'interno di una 
try/catch.
Al posto della connection la lambda vede il context e tutto il resto 
dei discorsi è uguale.
Con gli OR/M il numero di situazioni in cui l'errore è di logica e non 
tecnologico cresce. In altre parole puoi avere eccezioni lanciate da 
EF4 così come errori di logica (non eccezioni) dovuti alla complessità 
del grafo che stai persistendo/leggendo.
Ripetendo il discorso che ho già scritto, ha senso centralizzare la 
gestione dell'errore "tecnologico" fermo restando che ha anche senso 
che tu lanci (generando o rilanciando) eccezioni che siano 
application-specific.
Ma queste ultime sono scelte architetturali che vanno fatte in 
qualsiasi layer dell'applicazione e prescindono dal DAL in quanto tale.
