Le Regole d'Oro per lo sviluppo mobile - Secondo Episodio
Ho ricevuto diverse mail sul primo episodio con richieste di chiarimenti, soprattutto per il punto apertura connessioni; ho pensato di fare velocemente un secondo episodio sul tema:
Questo è quanto ho scritto a riguardo:
Fare poche Open/Close al DB - Con SQLCE il costo di apertura e chiusura di una connessione è alto
- Non esiste Connection Pooling per SQLCE
- Conviene quindi lasciare aperta la connessione al DB se si cercano le prestazioni
- Tenere quindi le connessioni statiche (con un singleton magari) nel Dal Layer
- E' anche vero che se le connessioni devono essere più di una (lavoriamo in multithread eseguendo operazioni in parallelo) diventa più complicata la loro gestione (e chiusura).
- Spesso, se non sono alla canna del gas, apro e chiudo le connessioni ogni volta
- Avendo un generatore di codice diventa semplice modificare questo comporamento
- Le connessioni comunque pesano quindi in applicazioni multi-thread pesare bene queste considerazioni
Come indicavo nell'ultimo punto, è importante pesare bene queste considerazioni; le performance sono importanti ed è importante cercare di massimizzarle nei punti critici dell'applicazione; è altrettanto importante la manutenibilità del codice e la valutazione delle controindicazioni.
Tenere aperte le connessioni significa migliorare le performance ma
- Spesso, in una applicazione, le operazioni che usano i dati fanno parte di un processo più ampio che parte con una azione da parte dell'utente; le performance percepite dall'utente in questo caso sono la cosa più imporante.
- Se riempiamo una griglia con 20 record paginati da una tabella con migliaia di record sicuramente il tempo di refresh della UI e il recupero delle informazioni (l'esecuzione della query) sono superiore all'apertura e chiusura della connessione
- Se l'operazione sui dati non è immediata (oltre 1 secondo per capirci) il tempo di apertura e chiusura è abbastanza ininfluente
- Normalmente i database SDF stanno su una memory card (Compact Flash o SD che sia) e spesso il palmare va in stand-by (GIUSTAMENTE) durante l'utilizzo: la filosofia su Windows CE consiste proprio nel sospendere e ripartire dall'esatto punto in cui si è lasciato il device questo vale per Pocket Outlook così come per le nostre applicazioni
- Se non usiamo il device, probabilmente il Power Save stacca la scheda SD per risparmiare batteria. Se abbiamo una connessione aperta è molto probabile che venga persa e l'applicazione non sia utilizzabile se non riaprendo la connessione
- Ancora peggio è utilizzare un SqlCeResultSet in binding su un controllo della user interface: in questo caso la connessione si perde durante lo stand-by o lo spegnimento della shceda...con un errore non proprio carino per l'utente...rivedere il primo episodio per altri problemi (prestazioni comprese) di un SqlCeResultSet
- Una connessione sempre aperta porta anche a "problemi" di organizzazione: ad esempio occorre chiuderla prima di una operazione di Compact, Repair, Verify
- In generale, come dicevo nel primo episodio, è vero che dal punto di vista delle performance pure converrebbe tenere sempre aperta una connessione, ma è altrettanto vero che i contro non sono pochi. Tutti "i contro" possono essere risolti da codice, ma, come dicevo, in generale, se non sono alla canna del gas :-) uso un mio Helper (una versione è disponibile sotto http://thinkmobile.it/files nella demo Sql 2005 Mobile Performance) che apre e chiude le connessioni.
- L'unica eccezione che uso per i comandi complessi è l'utilizzo di un comando Prepared (un esempio è sempre nella demo indicata in cui sono presente anche i tempi di accesso di tutti i metodi di accesso ai dati per una comparazione). In questo caso viene passato all'helper un parametro per non distruggere la connessione (in pratica non viene indicato il CommandBehavior.CloseConnection su DataReader) e una variabile statica per tenere vivo il comando preparato
- Ancora su SqlCeResultSet
- spesso una maschera che presenta una griglia apre una seconda maschera di dettaglio (una seconda griglia con il dettaglio ordini ad esempio o una maschera di editing della prima): con un resultset statico o key-driven ci sono dei lock sui record...cosa che nessuno di noi vuole avere durante un'update o peggio ancora una Delete del record dalla maschera secondaria
- Se facciamo operazioni in background, come ad esempio una Merge Replication, che succede alla nostra griglia (o ai campi in bind sulla user interface) mentre vengono aggiornati i dati dietro le quinte ? Vero che potremmo usare un ResultSet non aggiornabile e non-sensitive (Insensitive per l'enum)...ma in questo caso perchè non usare un DataReader ?
- Il punto 2 precedente è opinabile perchè un DataReader non consente binding...vero...però è altrettanto vero che non dovremmo mai portare oggetti "database" (classi di accesso ai dati) sulla User Interface...altrimenti ci leghiamo all'implementazione specifica del DB e non possiamo cambiare database (e metodi di accesso ai dati nelle tecnologie a venire) se non sbaraccando tutto
- Sempre a proposito di connessioni potrebbe essere sensato avere due connection string in una applicazione: una per leggere i dati (Mode=Read Only) e una tradizionale per letture/scritture: in questo modo possiamo evitare SLock inutili. Se usiamo Mode=Read Only occorre impostare una path per il TempDB tramite l'attributo Temp File Path.
- A proposito di TempDB occhio che se crasha SQLCE il tempdb resta nella posizione: occorre cancellarlo da codice. Il TempDB viene usato anche per tutte le query che non possono sfruttare un indice....ma non ce ne dovrebbero essere...