In questo primo articolo, faremo un’introduzione a uno strumento molto importante legato alla memorizzazione dei dati in maniera permanente nello storage del nostro dispositivo. Da sviluppatori, sapete bene che il supporto alla memorizzazione delle informazioni che siano impostazioni, o dati di altro tipo é molto importante. Al momento il Windows Runtime non dispone in maniera nativa il supporto ai Database, ma mette a disposizione la possibilità di serializzare/deserializzare i dati in diversi formati: xml e json. Seppur molto importanti e di semplice utilizzo, lo svantaggio è che tutte le informazioni devono comunque essere mantenute in memoria a scapito di rallentamenti e impatto sulle performance della nostra applicazione. Per questo motivo, torna utile l’approccio all’utilizzo del Database; un’ottima scelta è di ricorrere a Sqlite. Sqlite è un engine open source, creato e supportato dalla SQLite Consortiun supportato dal Windows Runtime grazie anche alla compatibilità con C++. Scritto in C++, garantisce ottime performance a livello di esecuzione e di qualunque operazione eseguiamo, lavora in modalità dati disconnessa, a differenza per esempio di Sqlserver, dove esiste un servizio chiamato DBMS per l’interazione con i dati. E’ multipiattaforma, quindi oltre Windows Phone Store, è compatibile con Windows Store, Android e altre piattaforme. Infine, è possibile utilizzare alcuni exstension methods di Linq (Language Integrated Query), per la precisione LinqToObject, che vedremo nel corso dell’articolo. Fino ad ora, è stata fatta una semplice introduzione su che cosa è Sqlite, ma per maggiori dettagli e delucidazioni, rimando alla fonte ufficiale. In quest’articolo vedremo nell’ordine:
- Installazione dell’engine di Sqlite
- Creazione del progetto
- Installazione di Sqlite-Net
- Creazione della classe per il Database
- Inserimento dei dati
- Aggiornamento dei dati
- Eliminazione dei dati
- Altre classi necessarie
- Inserimento dei namespace necessari
- Architettura di compilazione
- Test dell’applicazione
- Conclusione
Prima cosa da fare, non essendoci, come detto, il supporto ai dati in Windows Runtime, vanno installate alcune estensioni per interagire con Sqlite. La prima la troviamo a questo link. Facciamo attenzione a una cosa, in questo caso, installeremo l’estensione adatta a Windows Phone Store, ma se volessimo sviluppare un’applicazione Windows Store, dobbiamo installare l’engine adatto, poiché se abbiamo un progetto Universal App, si tratta non di una singola applicazione, ma siamo di fronte a un progetto che creerà alla fine due applicazioni distinte per piattaforma. Una volta scaricato e installato, lo aggiungeremo al progetto che pian piano andremo a creare nel corso dell’articolo.
E’ arrivato il momento di creare il nostro progetto di esempio. Avviamo VisualStudio 2013, nel mio caso uso la versione professional. Dal menù File, selezioniamo “Nuovo” e subito dopo “Progetto”. Creeremo un’applicazione usando come linguaggio di sviluppo C#. Nei template disponibili alla sezione “Applicazioni Windows Phone”, selezioniamo “Applicazione vuota (Windows Phone)” e assegniamo al progetto il nome “Sqlite Sample” come visibile in figura.
Attendiamo che l’ambiente di sviluppo sia inizializzato correttamente, e come anticipata nell’introduzione, andiamo ad aggiungere tutte le estensioni necessarie per interagire con Sqlite. Prima cosa, aggiungiamo il riferimento all’engine per Windows Phone, in esplora soluzioni, click con il tasto destro del Mouse su “Riferimenti”, e andiamo a selezionare “Aggiungi riferimento”. Nella finestra di dialogo successiva, sulla sinistra espandiamo la sezione “Estensioni”, se abbiamo installato correttamente l’engine, lo troveremo tra le estensioni disponibili, come mostrato in figura.
Adesso in concomitanza alla stesura dell’articolo la versione è la 3.8.7.4, per cui tutte le esercitazioni saranno con questa versione. Al momento, abbiamo installato il primo dei due elementi necessari, questo perché l’engine in se é scritto in C++, per cui per interagire richiederebbe che il progetto sia C++, dando poi non pochi problemi per chi non conosce tale linguaggio, o se abbiamo incluso il tutto in un progetto C# come nel nostro caso. Per questo motivo, sono state create delle librerie da diversi sviluppatori, in grado di astrarre quello che è in realtà l’engine di Sqlite, attraverso una serie di metodi e classi che aiutano lo sviluppatore a interagirne scrivendo codice con linguaggi di alto livello come C# e VB NET. Una di queste librerie si chiama Sqlite-net.
Con tale libreria, saremo in grado di eseguire tutte le operazioni che si fanno normalmente in un Database, come Insert, Delete, Update ed eseguire delle query di ricerca. Sqlite-net inoltre, offre un approccio tipico dell’Orm, si prenda come esempio LinqToSql o l’ultimo attuale EntityFramework. In più, ha il supporto a LinqToObject, per cui è possibile eseguire delle ricerche su collezioni di oggetti come Liste e ObservableCollection. Per maggiori dettagli, rimando alla documentazione ufficiale ed esempi che trovate a questo link. Essendo come detto una libreria di terze parti, dobbiamo aggiungerla al nostro progetto. Il modo più semplice è ricorrere a Nuget. Ritorniamo al nostro progetto, sempre in “Riferimenti”, tasto destro del mouse, selezioniamo il comando “Gestisci pacchetti Nuget”. Nella finestra di dialogo successiva, digitiamo nella casella di ricerca che troviamo in alto a destra “Sqlite-net”, come visibile in figura.
Dopo aver trovato la libreria, e fatto click sul pulsante “Installa”, se tutto va a buon fine, ha, com’è visibile in figura, un cerchio verde con il segno di spunta al suo interno, questo sta a significare che è stata installata correttamente. Abbiamo installato tutto il necessario; è ora di procedere alla creazione del database, ma prima guardiamo cosa è stato aggiunto dopo l’istallazione dell’engine di Sqlite e Sqlite-net.
Abbiamo il riferimento SQLite for Windows Phone 8.1 nei riferimenti, si tratta dell’engine che abbiamo aggiunto per primo, e i file SqLite.cs e SQLiteAsync.cs, appartenenti all’installazione della libreria Sqlite-net, con i quali saremo in grado di eseguire operazioni sul Database.
Come detto prima, Sqlite-net, offre un approccio tipico dell’orm, per cui non dovremmo preoccuparci del database, ma di creare la o le classi necessarie per poi eseguirne la creazione mediante la libreria e l’engine di Sqlite. Click con il mouse sul nome del progetto, quindi Sqlite Sample, tasto destro, e scegliamo il comando “Aggiungi” e dopo “Nuova Cartella”. La chiameremo “Classes”, al suo interno metteremo tutto ciò che riguarda l’interazione con il database e altro. Con la stessa procedura creiamo un’altra cartella, e la chiameremo “Screen”dove inseriremo, le schermate che compongono la nostra applicazione. Nella cartella Classes creiamo una nuova Classe denominata “Employee”, la procedura resta sempre quella per la creazione delle cartelle, ma scegliendo il comando “Classe”. All’interno del file inseriamo il codice che segue.
Com’è possibile notare, abbiamo semplicemente creato una classe chiamata Employee, che contiene quattro Proprietà, con degli attributi inseriti tra parentesi quadre, che daranno altra personalizzazione. E’ utile sapere che in questo caso, sarà creato all’interno del Database, una tabella con lo stesso nome della classe, quattro campi esattamente con lo stesso nome delle proprietà, sarà dedotto automaticamente il tipo di dato per i campi, varchar per Name e SurName, int per Id e Age. Inoltre con gli attributi, personalizziamo ulteriormente queste quattro proprietà, per il campo Id, gli diciamo mediante “PrimaryKey” che si tratta del campo contatore e che sarà aumentato automaticamente, questo mediante l’attributo “AutoIncrement”. Definiamo una lunghezza massima di trenta caratteri per i campi Name e SurName, e un massimo di tre numeri per il campo Age. Abbiamo definito una semplice classe che rappresenterà un dipendente, definendone il nome, cognome ed età. Nella schermata MainPage, definiamo una semplice interfaccia grafica che ci consentirà di accedere alle altre schermate. Copiamo questo codice xaml nel file MainPage.xaml.
Se tutto è inserito in maniera corretta, la nostra schermata iniziale deve avere quest’aspetto.
Passiamo ora all’editor di codice, con tasto F7, individuiamo il costruttore della classe MainPage, e inseriamo questo codice.
DatabaseManagement e una classe dentro della quale andremo a inserire tutta la gestione per l’interazione con il database. In questo caso stiamo andando a richiamare il Metodo CreateDatabase, il quale si occuperà di creare il Database qualora non esistesse nell’isolated storage. Questa è la parte di codice interessata che creerà il Database.
Definiamo una variabile denominata person di tipo SqliteAsyncConnection, che non è altro che la classe per la gestione della stringa di connessione al database, e mediante il metodo seguente:
andiamo a recuperare la variabile conn, che si trova all’interno del metodo ConnectionDb(), la quale ci restituisce la seguente riga di codice, che poi sarà il percorso di locazione del Database.
In questo modo, dopo l’esecuzione del codice, abbiamo creato nella cartella “Local” dell’isolated storage un file denominato “employee.db”. Vedremo in seguito tutto il codice all’interno di questa classe, perché ci servirà nell’esempio che andremo a creare. Per completare tutta la parte di codice concernente la schermata iniziale, andiamo a gestire l’evento tapped dei quattro pulsanti, inseriamo la parte di codice seguente.
Per chi ha avuto esperienza con le versioni precedenti, noterà delle differenze per quanto riguarda la navigazione tra pagine. Se in Windows Phone 7/8 si utilizzava il metodo Navigate della proprietà NavigationService, passando poi mediante url di tipo relativo il nome della pagina sulla quale accedere, con eventuali parametri per lo scambio d’informazioni tra pagine, sulle applicazioni Windows Phone Store l’approccio è leggermente differente. Passiamo direttamente al metodo Navigate il riferimento alla pagina alla quale vogliamo accedere, in altre parole typeof(Delete) per esempio. Abbiamo poi due overload, il primo rimane sempre il/i parametri da passare alla pagina alla quale navighiamo, l’ultimo invece dalla possibilità di stabilire con quale animazione intendiamo visualizzare la pagina al momento della visualizzazione.
Arrivati a questo punto, abbiamo implementato tutta la parte concernente, la creazione del database. Creiamo ora, tutto ciò che riguarda l’inserimento dei dati all’interno della tabella Employee. Tornando al progetto, poniamo il cursore sulla cartella “Screen”, tasto destro del mouse, e scegliamo il comando “Aggiungi” e subito dopo “Nuovo elemento”. Nella successiva finestra di dialogo, cerchiamo il template “Pagina base”, come mostrato in figura.
Creato il template e visualizzata la pagina, inserisce il seguente codice xaml per definirne l’interfaccia grafica.
Se tutto il codice è inserito correttamente, questo sarà l’aspetto della pagina.
A livello di codice, tutto ciò che dobbiamo gestire, non è altro che l’evento tapped del pulsante Insert. Con tasto F7, entriamo nell’editor di codice, e inseriamo tutto il necessario all’interno dell’evento tapped del pulsante.
Notiamo che abbiamo inserito un costrutto if; questo perché l’utente potrebbe non inserire alcun dato e toccare poi il pulsante inserendo così dei valori null. Per evitare ciò, è stata creata una nuova classe denominata Validations, dentro la quale vi sono dei metodi che non fanno altro che eseguire un controllo. Nel nostro caso sarà verificato che le TextBox siano tutte valorizzate, se solo una non lo fosse, avviseremo l’utente mediante una MessageDialog. Non ci soffermeremo ulteriormente sui metodi di questa classe, ma ci limiteremo a mostrare poi il codice, poiché l’articolo è dedicato a Sqlite. Ciò che interessa invece, è il codice che trova all’interno del costrutto else, che si occuperanno di eseguire l’inserimento dei dati all’interno della tabella Employee. Anche il metodo InsertData, lo troviamo nella classe DatabaseManagement, vediamo come funziona.
Il metodo è molto semplice, andiamo a creare un nuovo oggetto di tipo Employee, dove valorizzeremo le proprietà Name, SurName e Age con i valori dei parametri che il metodo InsertData richiede al momento in cui è invocato. In seguito richiamiamo il metodo asincrono InsertAsync(), facente della classe SqliteAsyncConnection, passando come parametro l’oggetto da aggiungere alla tabella Employee, ovvero la variabile newemployee. Il metodo ConnectionDb lo abbiamo già visto in precedenza, al momento della parte di codice per la creazione del Database.
La procedura di aggiornamento dei dati, è simile a quella per l’inserimento, se non per la differenza che dovremo recuperare il dato/i da modificare. Torniamo al nostro progetto, e con la procedura che abbiamo utilizzato per la creazione della pagina Insert, sempre nella cartella Screen, creiamone una denominata Update. Inseriamo come per la pagina insert il codice xaml per definirne l’interfaccia grafica
Anche in questo caso, se tutto il codice è inserito correttamente, questo, sarà l’aspetto finale della pagina.
Immagine 1.8 La schermata aggiornamento dati.
Come per l’inserimento dei dati, anche in questa circostanza, dobbiamo gestire l’evento tapped del pulsante Update più l’evento SelectionChanged del controllo ListBox, che si occuperà di visualizzare tutti i dati disponibili all’interno della tabella Employee, e l’override On NavigatedTo. Quest’ultimo è eseguito al momento in cui si accede alla pagina scelta dall’utente durante l’utilizzo dell’applicazione. Con il tasto F7, accediamo all’editor e inseriamo il codice seguente.
Analizziamo il codice dell’evento SelectionChanged del controllo ListBox. Abbiamo definito una nuova classe denominata Parametri_ricerca, dove al suo interno vi sono dei campi che ci serviranno come scambio d’informazioni tra pagine, fondamentalmente tornando al discorso della navigazione tra pagine, per lo scambio d’informazioni tra esse, abbiamo la possibilità di utilizzare i parametri all’interno del metodo Navigate, come abbiamo già visto, o il sistema da me utilizzato, in altre parole creare una classe statica e definire tutti i campi necessari per l’utilizzo. Valorizziamo il campo NewName, con il valore della proprietà SelectedValue del controllo ListBox, più i controlli TextBox della pagina. C’è una cosa alla quale bisogna prestare attenzione: la proprietà SelectedValue è di tipo Object e non può essere assegnata direttamente alla proprietà Text dei controlli TextBox, e nemmeno alla variabile NewName, poiché tutti sono di tipo String, ma bisogna fare un cast, in altre parole convertire il tipo Object restituito in Employee, poi mediante le proprietà della classe, avremo in ritorno il valore del tipo corretto. In altre parole Name, Surname e Age convertito in stringa mediante il metodo ToString(). Terminata la valorizzazione di tutti gli oggetti all’interno dell’evento SelectionChanged, vediamo il codice per l’aggiornamento dei dati.
Al tap sul pulsante btnUpdate, visualizziamo una MessageDialog all’utente, in cui è chiesta conferma per l’aggiornamento dei dati, se si facciamo ancora un altro controllo che tutte le TextBox siano correttamente valorizzate, abbiamo già visto questa procedura nella parte concernente l’inserimento dei dati. Ciò che interessa è il codice all’interno del costrutto else. Anche questo metodo lo troveremo nella classe DatabaseManagement.
Analizziamo il codice concernente, l’aggiornamento dei dati. Per la connessione e il recupero della tabella Employee, facciamo uso dell’oramai conosciuto ConnectionDb(), ma ecco che subentra quanto abbiamo accennato all’inizio dell’articolo, il supporto a Linq. Questa riga di codice:
Where(w => w.Name.Equals(_name)).FirstOrDefaultAsync();
fa uso dell’exstension methods Where(), dove in base ad una condizione, (nel nostro esempio vogliamo modificare il nome di un dipendente “employee”) data dal parametro _name, recuperiamo poi, mediante l’exstension methods FirstOrDefaultAsync() il dato corretto e assegniamo alla proprietà Name della variabile updateemployee di tipo Employee il valore di _newname, dove _newname corrisponde al nome modificato. Infine mediante il metodo UpdateAsync(), viene eseguito l’aggiornamento verso la tabella Employee presente all’interno del Database.
Dobbiamo ancora inserire il codice dell’override OnNavigatedTo, lo trovate all’interno della Registrazione di Navigation Helper presente nei file di codice .cs.
La riga di codice che interessa a noi è la prima ovvero il metodo LoadData(), presente all’interno della oramai conosciuta classe DatabaseManagement.
Si tratta di un task, con il quale vuole eseguire l’aggiornamento del controllo Listbox della pagina Update. Infatti, se notiamo il metodo, richiede un parametro di tipo ListBox. In altre parole dovremmo passare un riferimento di un controllo, nel nostro caso la ListBox lstUpdatePerson. Poi, dichiarata una lista di tipo Employee, eseguiamo la connessione al database e recuperiamo la tabella Employee mediante il metodo Table<>, ma la query vera e propria é eseguita nel momento che invochiamo l’istruzione await query.ToListAsync(). Questo metodo restituisce una collection di oggetti di tipo Employee che poi andiamo ad aggiungere alla lista employee mediante un ciclo foreach. Terminata l’interazione del ciclo, l’ultima cosa da fare, è assegnare al parametro box il risultato di employee. In questo modo quando accederemo alla schermata di Update, se vi sono presenti dati, li potremo visualizzare correttamente.
Il processo per l’eliminazione dei dati, è pressoché identico a quello per l’aggiornamento, l’unica differenza sta nel chiamare una query differente al momento in cui desideriamo eliminare il dato. Torniamo al nostro progetto iniziale, seguiamo la procedura che abbiamo già visto per la creazione delle schermate di Insert e Update, e aggiungiamo sempre nella cartella Screen una nuova pagina base e la denominiamo Delete. Aggiungiamo il seguente codice xaml per definirne l’interfaccia grafica.
Anche in questo caso, se tutto il codice è inserito correttamente, questo, sarà l’aspetto finale della pagina.
La gestione degli eventi è identica a quella che abbiamo visto per l’aggiornamento, abbiamo da gestire l’evento tapped del pulsante Delete, più l’evento SelectionChanged del controllo ListBox, e l’override On NavigatedTo. Con il tasto F7, accediamo all’editor e inseriamo il codice seguente.
Noteremo che la gestione dell’evento SelectionChanged è identica a quella vista in precedenza, l’unica differenza sta nell’evento tapped del Pulsante btnDelete, la quale richiama il metodo DeleteData che troviamo sempre nel file DatabaseManagement. Qui di seguito il codice:
Anche in questo caso, è tutto identico al metodo UpdateData, eccezion fatta per l’ultima riga di codice.
Come anticipato, l’unica differenza sta nel fatto che andiamo a eliminare un dato, per cui è invocato il metodo DeleteAsync(), che si occuperà di eliminare in base al valore del parametro _name il dato corrispondente. Manca ancora la gestione dell’override OnNavigatedTo, che abbiamo comunque visto in occasione della pagina di Update. Di seguito la parte di codice.
Ricordo che l’override OnNavigateTo, lo trovate all’interno della Registrazione di Navigation Helper presente nei file di codice .cs.
Per ora abbiamo gestito l’inserimento, l’aggiornamento ed eliminazione dei dati. Tuttavia se provassimo a compilare la Solution, otterremo errori di compilazione, questo perché vanno aggiunte altre classi per il supporto dei dati, la ricerca e il controllo valorizzazione prima delle operazioni sul database. Torniamo al nostro progetto, puntiamo il cursore sulla cartella Classes, tasto destro e creiamo una classe denominata DatabaseManagement, poi modifichiamo il codice attuale con questo.
Questo sarà il codice che ci permetterà di comunicare e interagire con il Database, lo abbiamo già visto prima. Con la stessa procedura di prima, creiamo una classe denominata Validations, e sostituiamo il codice esistente con questo.
Non ci soffermeremo a spiegare cosa esegue tale codice, perché esulerebbe dall’articolo; l’importante è sapere che vengono eseguiti dei controlli che ho accennato prima, in merito al fatto che tutti i controlli TextBox siano correttamente valorizzati, in più il controllo MessageDialog che si occupa di avvisare l’utente in caso di errori. Sempre nella cartella Classes, creiamo ancora una classe denominata Parametri ricerca, e anche qui sostituiamo il codice esistente con questo che segue.
Tornando al discorso della navigazione tra pagine, questo è il metodo che utilizzo per scambiare informazioni tra pagine all’interno dell’applicazione.
Altra cosa da fare, è includere i namespace necessari poiché ci sono due cartelle aggiuntive, e aggiunto al loro interno delle classi. Inseriamo nell’ordine:
Nelle schermate Insert, Update e Delete.xaml.cs
Nella schermata MainPage.xaml.cs
L’ultima cosa da fare, prima di compilare la Solution è cambiare l’architettura della piattaforma di destinazione. Questo è dato dal fatto che l’engine di Sqlite è scritto in C++, e di default la piattaforma di destinazione impostata nel progetto è Any CPU. Questa modalità non è supportata. Per fare ciò nel menù principale di VisualStudio abbiamo il comando “Compila”, poi “Gestione configurazione”, come mostrato in figura.
Nella successiva finestra di dialogo, notiamo che abbiamo diverse scelte di piattaforme, Any CPU (impostazione di default), ARM, x64 e x86.
Dobbiamo selezionare la piattaforma di destinazione seconda, dove stiamo provando l’applicazione. Se utilizziamo un tablet, o un telefono dotato di processore ARM, dobbiamo selezionare la piattaforma ARM. Se invece stiamo utilizzando l’emulatore nel caso di Windows Phone o, un PC nel caso di Windows, dobbiamo selezionare x86 o x64, tutto dipende dall’architettura del processore se 32 o 64 bit. Nel mio caso, ho provato l’applicazione di esempio su un Nokia Lumia 925, con sistema operativo Windows Phone 8.1, per cui ho scelto il modo di configurazione ARM.
A questo punto siamo pronti per compilare ed eseguire l’applicativo. Tasto F5, e se non ci sono problemi, questa è la schermata iniziale dell’applicazione.
Facciamo tap sul pulsante Insert sample page, fino a visualizzare la pagina schermata per l’inserimento dei dati.
Inseriamo uno o più nomi, cognomi ed età a piacimento, e subito dopo tap sul pulsante Insert. Torniamo indietro mediante la pressione sul pulsante Back. Non ho inserito alcuna MessageDialog in merito all’inserimento dei dati, solo per questioni di praticità. Tornati alla schermata principale, fare tap sul pulsante Update sample page, fino a visualizzare le pagine che ci consentono di eseguire un aggiornamento dei dati inseriti.
In questo caso, ho inserito due dipendenti all’interno della tabella Employee, com’è possibile notare in figura. Tocchiamo un item qualsiasi dal controllo ListBox fino a visualizzare questa condizione.
Nell’esempio andiamo a modificare per comodità, solo il nome del dipendente, ma modificando il codice del metodo UpdateData() che troviamo nel file DatabaseManagement, è possibile modificare anche il cognome ed età, aggiungendo altri parametri al metodo, e assegnarli poi alle proprietà Surname e Age alla variabile updateemployee. Modifichiamo a piacimento il contenuto della TextBox di fianco alla label “Name” e tap sul pulsante Update. Ci sarà chiesta conferma per l’aggiornamento, tap sul pulsante yes, e noteremo che sarà eseguito l’aggiornamento all’interno del controllo ListBox. Questo sta a significare che la procedura è andata a buon fine.
Ora non ci resta che provare la parte concernente l’eliminazione di un dato. Torniamo alla MainPage con il pulsante Back Button, e fare tap sul pulsante Delete sample page.
La procedura resta identica a quella per l’aggiornamento, con la differenza che stiamo andando a eliminare un dato all’interno della tabella Employee. Toccando un item all’interno del controllo ListBox, visualizzeremo il dettaglio nel controllo TextBox. Tap sul pulsante Delete, avremo la solita MessageDialog che ci chiede conferma prima di eliminare il dato, tap su yes ed ecco il risultato finale della procedura.
In questa prima parte, abbiamo visto le basi di Sqlite, come installare l’engine, la libreria Sqlite-net, e visto le operazioni più comuni (dette anche code first), come l’inserimento, l’aggiornamento e l’eliminazione dei dati da una tabella di un Database. Abbiamo visto che la libreria Sqlite-net ha un supporto a Linq, anche se al momento sono supportati pochi operatori. Abbiamo fatto uso dell’operatore Where() e FirstOrDefaultAsync(). Una cosa che al momento non è ancora supportata è la relazione tra tabelle, quindi gli join mediante chiavi esterne, ma per unire una o più tabelle tra di loro, per ora si è costretti a eseguire query multiple annidate. Questo però va a scapito della performance dell’applicazione. Nel prossimo articolo si vedrà come eseguire una ricerca all’interno di una tabella.