Indice dei Contenuti
Introduzione
Riprendiamo il nostro percorso di apprendimento su Sqlite. Prima di continuare vediamo in sintesi gli argomenti trattati nel precedente articolo.
Si è discusso sull’installazione dell’engine di Sqlite, la creazione del progetto di prova, l’installazione della libreria Sqlite-net disponibile su Nuget, come creare sullo Storage del telefono il Database al primo avvio dell’applicazione, per terminare con l’inserimento, la modifica e la cancellazione dei dati dal Database.
In questo articolo vedremo in che modo eseguire una ricerca di informazioni tra più tabelle.
La versione attuale di Sqlite è la 3.8.9 che trovate a questo link, noi ci baseremo ancora sulla versione 3.8.7.4, usata nell’articolo precedente, Vedremo nell’ordine:
- Creazione della schermata Finddata
- Creazione della schermata Result
- Creazione della classe Job
- Creazione della classe RoleUser
- Modifica della classe Parametri_ricerca
- Inserimento dei namespace necessari
- Implementazione del codice nella classe DatabaseManagement
- Modifica della classe Insert
- Modifica della classe Update
- Modifica della classe MainPage
- Test dell’applicazione
- Conclusione
- Altre risorse
Creazione della schermata Finddata
Prima di continuare, consiglio di leggere la prima parte in modo da aver chiaro cosa è stato eseguito. Tutto il codice di esempio insieme al progetto è disponibile a questo link.
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.
<Page
x:Class=”SqlLite_Sample.Screen.Finddata”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
xmlns:local=”using:SqlLite_Sample.Screen”
xmlns:d=”http://schemas.microsoft.com/expression/blend/2008″
xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006″
mc:Ignorable=”d”
Background=”{ThemeResource ApplicationPageBackgroundThemeBrush}”>
<Grid x:Name=”LayoutRoot”>
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”*”/>
</Grid.RowDefinitions>
<!– Pannello del titolo –>
<StackPanel Grid.Row=”0″ Margin=”19,0,0,0″>
<TextBlock Text=”Sqlite sample” Style=”{ThemeResource TitleTextBlockStyle}” Margin=”0,12,0,0″/>
<TextBlock Text=”Find data page” Margin=”0,-6.5,0,26.5″ Style=”{ThemeResource HeaderTextBlockStyle}” CharacterSpacing=”{ThemeResource PivotHeaderItemCharacterSpacing}”/>
</StackPanel>
<!–TODO: il contenuto deve essere inserito all’interno della seguente griglia–>
<Grid Grid.Row=”1″ x:Name=”ContentRoot” Margin=”19,9.5,19,0″>
<Grid.RowDefinitions>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”Auto”/>
</Grid.RowDefinitions>
<StackPanel x:Name=”splRadioButton” Grid.Row=”0″>
<RadioButton
x:Name=”rbnName”
IsChecked=”True”
Content=”Name”
Tapped=”rbnName_Tapped”/>
<RadioButton
x:Name=”rbnRole”
IsChecked=”False”
Content=”Role”
Tapped=”rbnName_Tapped”/>
<RadioButton
x:Name=”rbnAge”
IsChecked=”False”
Content=”Age”
Tapped=”rbnName_Tapped”/>
</StackPanel>
<StackPanel Grid.Row=”1″>
<TextBlock
x:Name=”tbkName”
FontSize=”20″
Text=”Find data”
HorizontalAlignment=”Center”
VerticalAlignment=”Center”/>
<TextBox
x:Name=”tbxFindForName”/>
</StackPanel>
<Button
Grid.Row=”2″
x:Name=”btnFind”
Content=”Find”
HorizontalAlignment=”Center”
Tapped=”btnFind_Tapped”/>
</Grid>
</Grid>
</Page>
Abbiamo tre controlli RadioButton che a seconda di cosa selezioniamo, possiamo eseguire una tipologia di ricerca, nel nostro caso per Nome, Ruolo e Età. Andremo a digitare un termine all’interno del controllo TextBox, posto sotto il controllo TextBlock Find data, e al termine dell’inserimento mediante il Button Find avvieremo la procedura di ricerca a seconda di cosa abbiamo scelto.
Con il tasto F7, entriamo all’interno dell’editor di codice e modifichiamo il costruttore della classe Findata come segue.
public Finddata()
{
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += this.NavigationHelper_LoadState;
this.navigationHelper.SaveState += this.NavigationHelper_SaveState;
foreach (var control in splRadioButton.Children.OfType<RadioButton>().Where(w => w.Content.Equals(“Name”)))
{
Parametri_ricerca.TypeSearch = control.Content.ToString();
}
}
Cosa viene eseguito? Non facciamo altro che impostare di default la ricerca per nome, ovvero quello di dare subito all’utente la possibilità di inserire un nome, il tutto andando a valorizzare la proprietà TypeSearch della classe Parametri_ricerca con il valore della proprietà Content del RadioButton che corrisponde a termine “Name”.
Dobbiamo ancora gestire l’evento Tapped del pulsante Find e l’evento Tapped dei controlli RadioButton.
Per fare ciò inseriamo all’interno della classe il codice C# seguente.
private async void btnFind_Tapped(object sender, TappedRoutedEventArgs e)
{
if (Validations.CheckTextBox(tbxFindForName).Equals(true))
{
var dialog = new MessageDialog(“Inserisci un termine di ricerca!”);
await dialog.ShowAsync();
if(Parametri_ricerca.TypeSearch.Equals(“”))
{
var dialog1 = new MessageDialog(“Inserisci un termine di ricerca!”);
await dialog1.ShowAsync();
}
}
else
{
Parametri_ricerca.FindData = tbxFindForName.Text;
Frame.Navigate(typeof(Result));
}
}
private void rbnName_Tapped(object sender, TappedRoutedEventArgs e)
{
var radiobuttonTapped = sender as RadioButton;
Parametri_ricerca.TypeSearch = radiobuttonTapped.Content.ToString();
}
Analizziamo il codice precedente, abbiamo l’evento Tapped del pulsante Find, con il quale eseguiremo due validazioni, la prima se all’interno del controllo TextBox vi è un valore, mentre la seconda sul valore della variabile TypeSearch.
Se una delle validazioni ha esito negativo, avvertiremo l’utente mediante una MessageDialog, se entrambe le validazioni hanno esito positivo, nel costrutto else, andiamo a valorizzare la porprietà FindData della classe Parametri_ricerca con il valore del controllo TextBox tbxFindForName e saremo poi condotti nella schermata Result della quale abbiamo accennato prima, mentre per l’evento Tapped dei controlli RadioButton recuperiamo mediante il parametro sender quale dei tre ha selezionato l’utente, valorizzando poi la variabile TypeSearch che assumerà uno tra i valori della porprietà Content di ogni singolo RadioButton.
Creazione della schermata Result
Terminata questa attività nella classe Finddata possiamo occuparci di creare la schermata Result. In esplora soluzioni, creiamo la schermata Result come abbiamo fatto per Finddata, e nel file xaml sostituiamo il codice esistente con il seguente:
<Page
x:Class=”SqlLite_Sample.Screen.Result”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
xmlns:local=”using:SqlLite_Sample.Screen”
xmlns:d=”http://schemas.microsoft.com/expression/blend/2008″
xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006″
mc:Ignorable=”d”
Background=”{ThemeResource ApplicationPageBackgroundThemeBrush}”>
<Grid x:Name=”LayoutRoot”>
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”*”/>
</Grid.RowDefinitions>
<!– Pannello del titolo –>
<StackPanel Grid.Row=”0″ Margin=”19,0,0,0″>
<TextBlock Text=”Sqlite sample” Style=”{ThemeResource TitleTextBlockStyle}” Margin=”0,12,0,0″/>
<TextBlock Text=”Result” Margin=”0,-6.5,0,26.5″ Style=”{ThemeResource HeaderTextBlockStyle}” CharacterSpacing=”{ThemeResource PivotHeaderItemCharacterSpacing}”/>
</StackPanel>
<!–TODO: il contenuto deve essere inserito all’interno della seguente griglia–>
<Grid Grid.Row=”1″ x:Name=”ContentRoot” Margin=”19,9.5,19,0″>
<Grid.RowDefinitions>
<RowDefinition Height=”Auto”/>
</Grid.RowDefinitions>
<ListBox Grid.Row=”0″ x:Name=”lstFindPerson”>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<StackPanel Orientation=”Horizontal”>
<TextBlock x:Name=”tbkName”
FontWeight=”Bold”
Text=”Name”/>
<TextBlock Width=”30″/>
<TextBlock x:Name=”tbkRole”
FontWeight=”Bold”
Text=”Role”/>
<TextBlock Width=”30″/>
<TextBlock x:Name=”tbkAge”
FontWeight=”Bold”
Text=”Age”/>
<TextBlock Height=”50″/>
</StackPanel>
<StackPanel Orientation=”Horizontal”>
<TextBlock
x:Name=”tbkFindForName”
Text=”{Binding Name}”/>
<TextBlock Width=”20″/>
<TextBlock
x:Name=”tbkFindForRole”
Text=”{Binding Role}”/>
<TextBlock Width=”20″/>
<TextBlock
x:Name=”tbkFindForAge”
Text=”{Binding Age}”/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
</Page>
Ecco come dovrà essere la schermata dopo l’inserimento del codice Xaml.
Ciò che abbiamo creato è semplicemente un controllo ListBox con al suo interno due controlli StackPanel.
Nel primo avremo tre controlli TextBlock con del testo statico, vale a dire Nome, Ruolo ed età, nel secondo StackPanel, vi sono tre controlli TextBox con la proprietà Text in binding alle proprietà Name, Role e Age di una collection di tipo RoleUser, ovvero la classe che si occuperà di mostrarci il risultato della ricerca, anche questa classe la creeremo nel corso dell’articolo.
Terminata la parte grafica, con il tasto F7 andiamo nell’editor di codice, modifichiamo il costruttore della classe Result come segue.
public Result()
{
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += this.NavigationHelper_LoadState;
this.navigationHelper.SaveState += this.NavigationHelper_SaveState;
DatabaseManagement.FindForName(Parametri_ricerca.FindData, Parametri_ricerca.TypeSearch, lstFindPerson);
}
La parte che interessa a noi è l’ultima riga di codice, in altre parole:
DatabaseManagement.FindForName(Parametri_ricerca.FindData, Parametri_ricerca.TypeSearch, lstFindPerson);
Questo metodo richiede tre parametri, il termine di ricerca, ovvero ciò che andremo ad inserire all’interno del controllo TextBox nella schermata Finddata, il secondo parametro è il tipo di ricerca, che può essere nel nostro caso per nome, ruolo o età.
L’ultimo parametro è il riferimento al controllo ListBox appartenente alla schermata Result. Vedremo il perché più avanti quando dobbiamo modificare il codice nella classe DatabaseManegement.
Creazione della classe Job
Dopo aver definito anche la schermata Result, è arrivato il momento di creare le classi necessarie per la funzione di ricerca.
Torniamo al progetto di prova, posizioniamo il cursore sopra la cartella Classes, tasto destro del mouse e scegliamo i comandi “Aggiungi” e subito dopo “Classe” e la denominiamo con il nome Job.
Questa classe non fa altro che aggiungere un ruolo per ogni utente che andremo ad inserire nel DataBase creato al primo avvio dell’applicazione, mediante una nuova tabella che avrà appunto il nome della classe che creeremo in questo passaggio.
A classe creata, andiamo a sostituire il codice C# attuale con il seguente.
using SQLite;
namespace SqlLite_Sample.Classes
{
class Job
{
[SQLite.PrimaryKey,AutoIncrement]
public int Id { get; set; }
[MaxLength(30)]
public string Name { get; set; }
[MaxLength(30)]
public string Role { get; set; }
[MaxLength(3)]
public int Age { get; set; }
}
}
Con questo codice abbiamo semplicemente definito quattro proprietà.
La prima, di tipo int, che sarà la chiave primaria. Questo mediante l’attributo PrimaryKey.Autoincrement, che penserà ad incrementare automaticamente il valore della proprietà Id.
La seconda proprietà sarà utilizzata per definire il nome, la terza il ruolo, e l’ultima l’età. Notiamo che per le proprietà Name e Role, è stato definito un attributo MaxLenght(30), ciò significa che entrambe le proprietà non potranno avere un valore stringa di oltre 30 caratteri, l’ultima di tipo int, serve per memorizzare l’età dell’utente, con un massimo di tre numeri mediante l’attributo MaxLenght(3).
Creazione della classe RoleUser
Creiamo l’ultima delle due classi necessarie con la stessa procedura che abbiamo usato precedentemente sempre nella cartella Calsses, con la differenza che stavolta la nomineremo RoleUser.
Andiamo a sostituire il codice C# attuale con il seguente.
using SQLite;
namespace SqlLite_Sample.Classes
{
class RoleUser
{
[MaxLength(30)]
public string Name { get; set; }
[MaxLength(30)]
public string Role { get; set; }
[MaxLength(3)]
public int Age { get; set; }
}
}
Anche in questa circostanza, abbiamo definito tre proprietà, due di tipo string e una di tipo int con gli attributi utilizzati per le proprietà della classe Job sul limite dei 3 e 30 caratteri. Questa è la classe che utilizzeremo in binding se ricordate sui controlli TextBox definiti nella classe Result.
Modifica della classe Parametri_ricerca
Dobbiamo ora modificare ed inserire alcune proprietà in questa classe, poiché ci serviranno nel corso dell’articolo per gestire l’update delle informazioni sull’utente.
Con il cursore posizionato sulla cartella Classes apriamo il file Parametri_ricerca.cs e modifichiamo il codice C# esistente con il seguente.
namespace SqlLite_Sample.Classes
{
public static class Parametri_ricerca
{
public static string FindData { get; set; }
public static string TypeSearch { get; set; }
public static string NewName { get; set; }
public static string NewSurName { get; set; }
public static int NewAge {get;set;}
}
}
Inserimento dei namespace necessari
Prima di modificare il codice C# della classe DataBaseManagement, dobbiamo inserire nelle classi Job e RoleUser il seguente Namespace.
using SqlLite_Sample.Classes;
Questo è necessario per poter fare uso della classe Parametri_ricerca, posizionata all’interno della cartella Classes.
Implementazione del codice nella classe DatabaseManagement
Andiamo ora a modificare il codice C# della classe DatabaseManagement. Andremo a modificare i metodi Insert, Delete e UpdateData in modo da gestire anche la nuova tabella che verrà creata al primo avvio, in altre parole la tabella Job.
In esplora soluzioni facciamo doppio click con il mouse sul file DatabaseManagement e modifichiamo il metodo CreateDatabase come segue.
public static async void CreateDatabase()
{
await ConnectionDb().CreateTableAsync<Employee>();
await ConnectionDb().CreateTableAsync<Job>();
}
E’ stata aggiunta una nuova riga di codice, che non farà altro che creare come anticipato una tabella denominata Job all’interno del Database people.db. Modifichiamo ora il metodo InsertData come segue.
public async static void InsertData(string _name, string _surname, int _age, string _role)
{
var newemployee = new Employee
{
Name = _name,
SurName = _surname,
Age = _age,
};
var newjob = new Job
{
Name = newemployee.Name,
Role = _role,
Age = newemployee.Age,
};
await ConnectionDb().InsertAsync(newemployee);
await ConnectionDb().InsertAsync(newjob);
}
Analizziamo il codice in dettaglio.
A differenza dell’articolo precedente, abbiamo aggiunto un quarto parametro al metodo, dove va inserito il ruolo dell’utente, e una nuova istanza della classe Job, valorizzando le proprietà Name,Role e Age rispettivamente con i valori di newemployee.Name, il parametro _role del metodo InsertData e newemployee.Age, e infine andiamo a memorizzare e rendere così permanenti le informazioni nel database mediante il metodo InserAsync, passando come parametri le istanze delle classi Employee e Job.
Dobbiamo modificare anche il codice del metodo DeleteData in modo da rimuovere da entrambe le tabelle del database l’utente in questione, il metodo DeleteData va modificato come segue:
public async static void DeleteData(string _name)
{
var deleteemployee = await ConnectionDb().Table<Employee>().Where(w => w.Name.Equals(_name)).FirstOrDefaultAsync();
deleteemployee.Name = _name;
await ConnectionDb().DeleteAsync(deleteemployee);
var deleterole = await ConnectionDb().Table<Job>().Where(w => w.Id.Equals(deleteemployee.Id)).FirstOrDefaultAsync();
await ConnectionDb().DeleteAsync(deleterole);
}
E’ stata aggiunta questa parte di codice
var deleterole = await ConnectionDb().Table<Job>().Where(w => w.Id.Equals(deleteemployee.Id)).FirstOrDefaultAsync();
await ConnectionDb().DeleteAsync(deleterole);
dove andiamo a verificare mediante il metodo Equals le proprietà Id delle tabelle Employee e Job, se viene trovata una corrispondenza, i dati dell’utente saranno rimossi dalla tabella Job, il tutto con il metodo DeleteAsync, passando come argomento la variabile deleterole di tipo Job.
Modifichiamo ora il codice del metodo UpdateData, sostituendo quello attuale con il seguente.
public async static void UpdateData(string _name, string _newname, string _surname, string _newsurname, int _age, int _newage, string _newrole)
{
var updateemployee = await ConnectionDb().Table<Employee>().Where(w => w.Name.Equals(_name) && w.SurName.Equals(_surname) && w.Age.Equals(_age)).FirstOrDefaultAsync();
updateemployee.Name = _newname;
updateemployee.SurName = _newsurname;
updateemployee.Age = _newage;
await ConnectionDb().UpdateAsync(updateemployee);
var updaterole = await ConnectionDb().Table<Job>().Where(w=> w.Id.Equals(updateemployee.Id)).FirstOrDefaultAsync();
updaterole.Name = updateemployee.Name;
updaterole.Age = updateemployee.Age;
updaterole.Role = _newrole;
await ConnectionDb().UpdateAsync(updaterole);
}
E stata aggiunta la gestione per l’update della tabella Job esattamente come per il metodo DeleteData. In altre parole andando a verificare la proprietà Id con il metodo Equals, e se la comparazione tra le due proprietà ha esito positivo, le proprietà Name, Age e Role della tabella Job assumeranno i nuovi valori di updateemployee,Name, updateemployee.Age e il parametro_newrole.
Inseriamo ora il metodo FindForName; questo metodo non c’era nel precedente articolo, in quanto serve per eseguire la ricerca vera e propria.
public async static void FindForName(string _name, string _typesearch, ListBox _box)
{
var employee = new List<Employee>();
var job = new List<Job>();
var roleuser = new List<RoleUser>();
var queryName = ConnectionDb().Table<Employee>();
var resultName = await queryName.ToListAsync();
var queryJob = ConnectionDb().Table<Job>();
var resultjob = await queryJob.ToListAsync();
foreach (var findperson in resultName)
{
employee.Add(new Employee { Name = findperson.Name, SurName = findperson.SurName, Age = findperson.Age });
}
foreach (var findjob in resultjob)
{
job.Add(new Job { Name = findjob.Name, Role = findjob.Role, Age = findjob.Age });
}
switch (_typesearch)
{
case “Name”:
var resultForName = employee
.Join(job, newname => newname.Id, newjob => newjob.Id, (newname, newjob) =>
new { newjob.Name, newjob.Role, newjob.Age }).Where(w => w.Name.Equals(_name))
.Distinct();
foreach (var newjob in resultForName)
{
roleuser.Add(new RoleUser { Name = newjob.Name, Role = newjob.Role, Age = newjob.Age });
}
break;
case “Role”:
var resultForRole = employee
.Join(job, newname => newname.Id, newjob => newjob.Id, (newname, newjob) =>
new { newjob.Name, newjob.Role, newjob.Age }).Where(w => w.Role.Equals(_name))
.Distinct();
foreach (var newjob in resultForRole)
{
roleuser.Add(new RoleUser { Name = newjob.Name, Role = newjob.Role, Age = newjob.Age });
}
break;
case “Age”:
var resultAge = employee
.Join(job, newname => newname.Id, newjob => newjob.Id, (newname, newjob) =>
new { newjob.Name, newjob.Role, newjob.Age }).Where(w => w.Age.Equals(int.Parse(_name)))
.Distinct();
foreach (var newjob in resultAge)
{
roleuser.Add(new RoleUser { Name = newjob.Name, Role = newjob.Role, Age = newjob.Age });
}
break;
}
_box.ItemsSource = roleuser;
}
Analizziamo il codice precedente.
Sono state definite tre collection: una di tipo Employee, una di tipo Job e l’ultima di tipo RoleUser.
var employee = new List<Employee>();
var job = new List<Job>();
var roleuser = new List<RoleUser>();
Queste collection ad eccezione di quella di tipo RoleUser, saranno valorizzate poi con tutti i dati provenienti dalle tabelle Employee e Job presenti nel Database people.db.
Successivamente andiamo a recuperare le tabelle Employee e Job mediante questo codice
var queryName = ConnectionDb().Table<Employee>();
var queryJob = ConnectionDb().Table<Job>();
esattamente con il metodo Table<T>(), andiamo a recuperare le informazioni vere e proprie presenti nelle tabelle. Per ultimo convertiamo tutto il contenuto in una Lista con le seguenti righe di codice.
var resultName = await queryName.ToListAsync();
var resultjob = await queryJob.ToListAsync();
Così facendo, convertiamo le informazioni all’interno della tabella in una collection che possiamo poi manipolare a piacimento secondo nostre esigenze.
Poiché come detto precedentemente la libreria SqliteNet non supporta pienamente tutti gli Extension Method di Linq e le relazioni tra tabelle, per cui dovremo bypassare in altro modo il problema, lo faremo con due cicli foreach valorizzando le collection employee e job create inizialmente.
foreach (var findperson in resultName)
{
employee.Add(new Employee { Name = findperson.Name, SurName = findperson.SurName, Age = findperson.Age });
}
foreach (var findjob in resultjob)
{
job.Add(new Job { Name = findjob.Name, Role = findjob.Role, Age = findjob.Age });
}
Osservando il codice precedente, capiremo il perché abbiamo dovuto convertire in una lista le informazioni della tabelle con il metodo ToListAsync() esposto dalla libreria SqliteNet.
Abbiamo ora in memoria tutti i dati per poter eseguire la ricerca desiderata. Analizziamo ora la parte più importante del metodo FindForName, il costrutto switch.
switch (_typesearch)
{
case “Name”:
var resultForName = employee
.Join(job, newname => newname.Id, newjob => newjob.Id, (newname, newjob) =>
new { newjob.Name, newjob.Role, newjob.Age }).Where(w => w.Name.Equals(_name))
.Distinct();
foreach (var newjob in resultForName)
{
roleuser.Add(new RoleUser { Name = newjob.Name, Role = newjob.Role, Age = newjob.Age });
}
break;
case “Role”:
var resultForRole = employee
.Join(job, newname => newname.Id, newjob => newjob.Id, (newname, newjob) =>
new { newjob.Name, newjob.Role, newjob.Age }).Where(w => w.Role.Equals(_name))
.Distinct();
foreach (var newjob in resultForRole)
{
roleuser.Add(new RoleUser { Name = newjob.Name, Role = newjob.Role, Age = newjob.Age });
}
break;
case “Age”:
var resultAge = employee
.Join(job, newname => newname.Id, newjob => newjob.Id, (newname, newjob) =>
new { newjob.Name, newjob.Role, newjob.Age }).Where(w => w.Age.Equals(int.Parse(_name)))
.Distinct();
foreach (var newjob in resultAge)
{
roleuser.Add(new RoleUser { Name = newjob.Name, Role = newjob.Role, Age = newjob.Age });
}
break;
}
Andiamo a verificare il valore della variabile _typesearch, che può assumere tre valori, Name, Role o Age. In ognuno dei casi va ad eseguire una query Linq sulle collection employee e job, non staremo a spiegare pienamente Linq perché esula dall’articolo in questione.
Le query non fanno altro che unire mediante l’extension method join le collection mediante la proprietà Id di entrambe, e infine con l’extension method where andiamo a verificare una determinata condizione, ovvero il valore del parametro _name, se viene trovata una o più corrispondenze saranno selezionate ed estrapolate le informazioni che corrispondono alla condizione specificata.
Infine valorizziamo la collection roleuser con il risultato finale della query con i valori Name, Role ed Age e terminiamo con il valorizzare il parametro _box di tipo ListBox con il valore della collection roleuser, ed ecco spiegato perché passiamo al metodo FindForName il riferimento del controllo ListBox presente nella schermata Result.
_box.ItemsSource = roleuser;
Modifica della classe Insert
Abbiamo terminato le modifiche nella classe DatabaseManagement, dobbiamo ancora eseguire degli accorgimenti nelle schermate di Insert e Update e MainPage, vedremo in dettaglio cosa.
Partiamo dalla classe Insert, aggiungeremo un controllo TextBox dove abbiamo la possibilità di inserire un ruolo per ogni nuovo utente che aggiungiamo nella tabella Job del Database people.db.
Torniamo al nostro progetto, posizioniamo il cursore sulla cartella Screen, e facciamo doppio click con il mouse sul file Insert.xaml. Aperto il file sostituiamo il codice Xaml esistente con questo.
<Page
x:Class=”SqlLite_Sample.Screen.Insert”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
xmlns:local=”using:SqlLite_Sample.Screen”
xmlns:d=”http://schemas.microsoft.com/expression/blend/2008″
xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006″
mc:Ignorable=”d”
Background=”{ThemeResource ApplicationPageBackgroundThemeBrush}”>
<Grid x:Name=”LayoutRoot”>
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”*”/>
</Grid.RowDefinitions>
<!– Pannello del titolo –>
<StackPanel Grid.Row=”0″ Margin=”19,0,0,0″>
<TextBlock
Text=”Sqlite sample”
Style=”{ThemeResource TitleTextBlockStyle}”
Margin=”0,12,0,0″/>
<TextBlock
Text=”Insert page”
Margin=”0,-6.5,0,26.5″
Style=”{ThemeResource HeaderTextBlockStyle}”
CharacterSpacing=”{ThemeResource PivotHeaderItemCharacterSpacing}”/>
</StackPanel>
<!–TODO: il contenuto deve essere inserito all’interno della seguente griglia–>
<Grid Grid.Row=”1″ x:Name=”ContentRoot” Margin=”19,9.5,19,0″>
<Grid.RowDefinitions>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”Auto”/>
</Grid.RowDefinitions>
<Grid Grid.Row=”0″>
<Grid.RowDefinitions>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”Auto”/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=”Auto”/>
<ColumnDefinition Width=”*”/>
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column=”0″
Grid.Row=”0″
x:Name=”tbkName”
FontSize=”25″
Text=”Name”
VerticalAlignment=”Center”
/>
<TextBlock
Grid.Column=”0″
Grid.Row=”1″
x:Name=”tbkSurname”
FontSize=”25″
Text=”Surname”
VerticalAlignment=”Center”
/>
<TextBlock
Grid.Column=”0″
Grid.Row=”2″
x:Name=”tbkAge”
FontSize=”25″
Text=”Age”
VerticalAlignment=”Center”
/>
<TextBlock
Grid.Column=”0″
Grid.Row=”3″
x:Name=”tbkRole”
FontSize=”25″
Text=”Role”
VerticalAlignment=”Center”
/>
<TextBox
Grid.Column=”1″
Grid.Row=”0″
x:Name=”tbxName”
/>
<TextBox
Grid.Column=”1″
Grid.Row=”1″
x:Name=”tbxSurname”
/>
<TextBox
Grid.Column=”1″
Grid.Row=”2″
x:Name=”tbxAge”
InputScope=”Number”
/>
<TextBox
Grid.Column=”1″
Grid.Row=”3″
x:Name=”tbxRole”
/>
</Grid>
<Grid Grid.Row=”1″>
<Grid.RowDefinitions>
<RowDefinition Height=”20″/>
<RowDefinition Height=”Auto”/>
</Grid.RowDefinitions>
<Button
Grid.Row=”1″
x:Name=”btnInsert”
Content=”Insert”
HorizontalAlignment=”Center”
Tapped=”btnInsert_Tapped”
/>
</Grid>
</Grid>
</Grid>
</Page>
Questa dovrà essere la nuova interfaccia grafica dopo la modifica del codice Xaml.
Terminata la parte grafica, tasto F7 per accedere all’editor di codice e modifichiamo l’evento Tapped del Button Insert come segue.
private async void btnInsert_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
{
if(Validations.CheckTextBox(tbxName,tbxSurname,tbxAge).Equals(true))
{
var dialog = new MessageDialog(“Valorizzare tutti i campi”);
await dialog.ShowAsync();
}
else
{
DatabaseManagement.InsertData(tbxName.Text, tbxSurname.Text, int.Parse(tbxAge.Text),tbxRole.Text);
}
}
La differenza sta in questa riga di codice
DatabaseManagement.InsertData(tbxName.Text, tbxSurname.Text, int.Parse(tbxAge.Text),tbxRole.Text);
Se ricordate nel metodo InsertData abbiamo aggiunto un quarto parametro necessario per inserire il ruolo dell’utente, ed ecco il perché di questa modifica anche nell’evento Tapped del Button.
Modifica della classe Update
Come la classe Insert, anche la classe Update necessita di alcune modifiche, e dato dal fatto che nell’articolo precedente era possibile modificare solo il nome dell’utente, daremo la possibilità di modificare il cognome, l’età e inserire anche un nuovo ruolo.
Sempre con il cursore posizionato sulla cartella Screen, apriamo il file Update.xaml, e andiamo a modificare il codice xaml esistente con questo.
<Page
x:Class=”SqlLite_Sample.Screen.Update”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
xmlns:local=”using:SqlLite_Sample.Screen”
xmlns:d=”http://schemas.microsoft.com/expression/blend/2008″
xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006″
mc:Ignorable=”d”
Background=”{ThemeResource ApplicationPageBackgroundThemeBrush}”>
<Grid x:Name=”LayoutRoot”>
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”*”/>
</Grid.RowDefinitions>
<!– Pannello del titolo –>
<StackPanel Grid.Row=”0″ Margin=”19,0,0,0″>
<TextBlock Text=”Sqlite sample” Style=”{ThemeResource TitleTextBlockStyle}” Margin=”0,12,0,0″/>
<TextBlock Text=”Update page” Margin=”0,-6.5,0,26.5″ Style=”{ThemeResource HeaderTextBlockStyle}” CharacterSpacing=”{ThemeResource PivotHeaderItemCharacterSpacing}”/>
</StackPanel>
<!–TODO: il contenuto deve essere inserito all’interno della seguente griglia–>
<Grid Grid.Row=”1″ x:Name=”ContentRoot” Margin=”19,9.5,19,0″>
<Grid.RowDefinitions>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”Auto”/>
</Grid.RowDefinitions>
<ListBox Grid.Row=”0″ x:Name=”lstUpdatePerson” SelectionChanged=”lstUpdatePerson_SelectionChanged”>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<StackPanel Orientation=”Horizontal”>
<TextBlock x:Name=”tbkName”
FontWeight=”Bold”
Text=”Name”/>
<TextBlock Width=”30″/>
<TextBlock x:Name=”tbkSurname”
FontWeight=”Bold”
Text=”Surname”/>
<TextBlock Width=”30″/>
<TextBlock x:Name=”tbkAge”
FontWeight=”Bold”
Text=”Age”/>
<TextBlock Height=”50″/>
</StackPanel>
<StackPanel Orientation=”Horizontal”>
<TextBlock
x:Name=”tbkFindForName”
Text=”{Binding Name}”/>
<TextBlock Width=”20″/>
<TextBlock
x:Name=”tbkFindForSurName”
Text=”{Binding SurName}”/>
<TextBlock Width=”20″/>
<TextBlock
x:Name=”tbkFindForAge”
Text=”{Binding Age}”/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBlock Grid.Row=”3″ x:Name=”tbkNewData” Text=”New data” HorizontalAlignment=”Center”/>
<StackPanel Grid.Row=”4″ x:Name=”splNewData”>
<StackPanel x:Name=”splNewName” Orientation=”Horizontal”>
<TextBlock Text=”Name” VerticalAlignment=”Center”/>
<TextBlock Width=”30″/>
<TextBox x:Name=”tbxNewName” Width=”Auto”/>
</StackPanel>
<StackPanel x:Name=”splNewSurName” Orientation=”Horizontal”>
<TextBlock Text=”SurName” VerticalAlignment=”Center”/>
<TextBlock Width=”15″/>
<TextBox x:Name=”tbxNewSurName” Width=”Auto”/>
</StackPanel>
<StackPanel x:Name=”splNewAge” Orientation=”Horizontal”>
<TextBlock Text=”Age” VerticalAlignment=”Center”/>
<TextBlock Width=”40″/>
<TextBox x:Name=”tbxNewAge” Width=”Auto” InputScope=”Number”/>
</StackPanel>
<StackPanel x:Name=”splNewRole” Orientation=”Horizontal”>
<TextBlock Text=”Role” VerticalAlignment=”Center”/>
<TextBlock Width=”40″/>
<TextBox x:Name=”tbxNewRole” Width=”Auto”/>
</StackPanel>
</StackPanel>
<Button Grid.Row=”6″ x:Name=”btnUpdatePerson” Content=”Update” Tapped=”btnUpdatePerson_Tapped”/>
</Grid>
</Grid>
</Page>
Ecco la nuova schermata Update dopo le modifiche
Modifichiamo ora anche la parte di codice C#, tasto F7 per accedere all’editor, e andiamo a sostituire tutto il contenuto degli eventi Tapped del button btnUpdatePerson e SelectionChanged del controllo ListBox lstUpdatePerson.
private async void btnUpdatePerson_Tapped(object sender, TappedRoutedEventArgs e)
{
await Validations.MessageConfirmDeleteoUpdatePerson(“Vuoi aggiornare i dati?”);
if (Validations.result.Equals(true))
{
if (Validations.CheckTextBox(tbxNewName, tbxNewSurName, tbxNewAge).Equals(true))
{
var dialog = new MessageDialog(“Valorizzare tutti i campi”);
await dialog.ShowAsync();
}
else
{
DatabaseManagement.UpdateData(Parametri_ricerca.NewName,tbxNewName.Text,
Parametri_ricerca.NewSurName,tbxNewSurName.Text,Parametri_ricerca.NewAge, int.Parse(tbxNewAge.Text),tbxNewRole.Text);
}
}
}
Resta tutto invariato eccetto questa riga di codice
DatabaseManagement.UpdateData(Parametri_ricerca.NewName,tbxNewName.Text,
Parametri_ricerca.NewSurName,tbxNewSurName.Text,Parametri_ricerca.NewAge, int.Parse(tbxNewAge.Text),tbxNewRole.Text);
Anche in questo caso, se ricordate è stato modificato il metodo UpdateData inserendo il supporto alla modifica del cognome, età e la possibilità di inserire o modificare il ruolo dell’utente.
Modifichiamo ancora il codice per il controllo ListBox
private void lstUpdatePerson_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Parametri_ricerca.NewName = ((Employee)(lstUpdatePerson.SelectedValue)).Name;
Parametri_ricerca.NewSurName = ((Employee)(lstUpdatePerson.SelectedValue)).SurName;
Parametri_ricerca.NewAge = ((Employee)(lstUpdatePerson.SelectedValue)).Age;
tbxNewName.Text = ((Employee)(lstUpdatePerson.SelectedValue)).Name;
tbxNewSurName.Text = ((Employee)(lstUpdatePerson.SelectedValue)).SurName;
tbxNewAge.Text = ((Employee)(lstUpdatePerson.SelectedValue)).Age.ToString();
}
Anche in questo caso abbiamo aggiunto il supporto per la modifica del cognome ed età aggiungendo queste righe di codice che non fanno altro che valorizzare i parametri del metodo UpdateData.
Parametri_ricerca.NewSurName = ((Employee)(lstUpdatePerson.SelectedValue)).SurName;
Parametri_ricerca.NewAge = ((Employee)(lstUpdatePerson.SelectedValue)).Age;
tbxNewSurName.Text = ((Employee)(lstUpdatePerson.SelectedValue)).SurName;
tbxNewAge.Text = ((Employee)(lstUpdatePerson.SelectedValue)).Age.ToString();
Modifica della classe MainPage
Ultima modifica da eseguire, la schermata iniziale dell’applicazione, aggiungiamo un button così da poter accedere alla schermata Finddata creata precedentemente. Modifichiamo il codice xaml come segue.
<Page
x:Class=”SqlLite_Sample.MainPage”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
xmlns:local=”using:SqlLite_Sample”
xmlns:d=”http://schemas.microsoft.com/expression/blend/2008″
xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006″
mc:Ignorable=”d”
Background=”{ThemeResource ApplicationPageBackgroundThemeBrush}”>
<Grid x:Name=”LayoutRoot”>
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”*”/>
</Grid.RowDefinitions>
<!– Pannello del titolo –>
<StackPanel Grid.Row=”0″ Margin=”19,0,0,0″>
<TextBlock Text=”Sqlite sample” Style=”{ThemeResource TitleTextBlockStyle}” Margin=”0,12,0,0″/>
<TextBlock Text=”Main page” Margin=”0,-6.5,0,26.5″ Style=”{ThemeResource HeaderTextBlockStyle}” CharacterSpacing=”{ThemeResource PivotHeaderItemCharacterSpacing}”/>
</StackPanel>
<!–TODO: il contenuto deve essere inserito all’interno della seguente griglia–>
<Grid Grid.Row=”1″ x:Name=”ContentRoot” Margin=”19,9.5,19,0″>
<StackPanel>
<Button
x:Name=”btnInsertSample”
Content=”Insert sample page”
Tapped=”btnInsertSample_Tapped”
Width=”300″/>
<Button
x:Name=”btnUpdateSample”
Content=”Update sample page”
Tapped=”btnUpdateSample_Tapped”
Width=”300″/>
<Button
x:Name=”btnDeleteSample”
Content=”Delete sample page”
Tapped=”btnDeleteSample_Tapped”
Width=”300″/>
<Button
x:Name=”btnFindSample”
Content=”Find sample page”
Tapped=”btnFindSample_Tapped”
Width=”300″/>
</StackPanel>
</Grid>
</Grid>
</Page>
Questa sarà la nuova schermata iniziale dell’applicazione con il nuovo button.
Terminata la modifica alla parte grafica, possiamo procedere con aggiungere l’evento tapped del nuovo button Find sample page.
Tasto F7, entrati nell’editor di codice aggiungiamo il codice che segue subito sotto l’evento tapped btnDeleteSample_Tapped.
private void btnFindSample_Tapped(object sender, TappedRoutedEventArgs e)
{
Frame.Navigate(typeof(Finddata));
}
Test dell’applicazione
Abbiamo terminato tutte le modifiche necessarie, possiamo ora precedere con il test dell’applicazione.
Per prima cosa dobbiamo modificare come detto e spiegato già nel primo articolo il target di compilazione su piattaforma ARM.
Lo possiamo eseguire o dal menù “Compila” scegliendo poi “Gestione configurazione”, o dal controllo Toolstrip selezionando la piattaforma ARM dal controllo combobox.
Terminata quest’attività, tasto F5 e avviamo il debug. Una volta che l’applicazione è avviata facciamo tap sul Button Insert sample page.
Entrati nella schermata successiva, inseriamo uno o più nomi a piacimento valorizzando tutti i controlli TextBox e confermando poi l’inserimento mediante il Button Insert, come mostrato in figura.
Ho inserito in sequenza questi tre utenti e le loro informazioni sono state memorizzate all’interno delle tabelle Employee e Job presenti all’interno del Database people.db.
Terminato l’inserimento dei dati, torniamo alla schermata principale e facciamo un tap sul button Find sample page. Una volta condotti nella schermata di ricerca, noteremo che per impostazione predefinita è selezionata la ricerca per nome.
Lasciamo così le impostazioni e digitiamo all’interno del controllo TextBox il nome di un utente che abbiamo inserito precedentemente.
Terminato l’inserimento, facciamo un tap sul button Find, e se viene trovata la corrispondenza quando saremo condotti nella schermata Result troveremo le informazioni sull’utente che abbiamo scelto per la ricerca, diversamente non visualizzeremo nulla. Io inserirò il nome “Carmelo”, e questo e il risultato della ricerca.
Sono state provate tutte le possibili combinazioni di ricerca, per nome, ruolo ed età, con i risultati che ci aspettavamo nella schermata di result, il tutto mediante il metodo FindForName che abbiamo inserito nella classe DatabaseManagement.
Resta ancora una funzionalità da provare, ovvero l’update delle informazioni che abbiamo inserito.
Torniamo nella schermata principale, e facciamo un tap Update sample page, entrati nella schermata visualizzeremo tutte le informazioni sugli utenti nel controllo ListBox. Facciamo un tap su un item e saranno valorizzate le TextBox sottostanti ad eccezione di quella della mansione come visibile nella figura seguente.
E stato modificato il ruolo da Verniciatore a Developer e successivamente abbiamo eseguito una ricerca specificando nel controllo TextBox della schermata Find data page il termine “Developer”, e nella schermata Result visualizziamo le informazioni in base al criterio di ricerca desiderato.
Conclusione
In questa seconda parte, abbiamo modificato il progetto del primo articolo secondo esigenze, inserendo:
- un nuovo metodo denominato FindForName nella classe DatabaseManagement
- creato due nuove schermate, ovvero Find data page e Result
- creato due classi Job e RoleUser, la prima per poter salvare tutto ciò che riguarda il Ruolo dell’utente, la seconda per mostrare i risultati di ricerca
- infine abbiamo visto come eseguire una ricerca all’interno delle tabelle in un Database Sqlite trasferendo però in contenuto delle tabelle in due collection, poiché come detto precedentemente la libreria SqliteNet non supporta tutti gli Extension method di Linq e le relazioni tra tabelle.
Nel prossimo articolo vedremo come è possibile Inserire, eliminare e aggiornare più dati contemporaneamente, sfruttando alcuni dei metodi che la libreria SqliteNet mette a disposizione di noi sviluppatori.