Archiviare i dati in locale con SQLite
L'archiviazione dei dati in SQLite è utile quando sono presenti dati relazionali. Si supponga di creare un'app di social media. È necessario archiviare le informazioni relative agli abbonati all'app. Tali dati includono un ID univoco per ogni utente e il relativo nome. È possibile modellare facilmente questo tipo di relazione in un database SQLite.
Questa unità descrive come usare SQLite in un'applicazione .NET MAUI tramite SQLite-net.
Che cos'è SQLite?
SQLite è un database locale multipiattaforma leggero che rappresenta uno standard di settore per le applicazioni per dispositivi mobili. SQLite non richiede un server. Il database è archiviato in un unico file su disco nel file system del dispositivo. Tutte le operazioni di lettura e scrittura vengono eseguite direttamente sulla base del file su disco di SQLite.
Le librerie native SQLite sono integrate in Android e iOS per impostazione predefinita; Tuttavia, il motore supporta solo un'API C/C++. Questo scenario non è ideale per gli sviluppatori .NET, che vogliono ottenere una certa interazione tra SQLite e .NET.
Cos'è SQLite-net?
Esistono diversi wrapper C# basati sul motore SQLite nativo che gli sviluppatori .NET possono usare. Molti sviluppatori .NET usano un wrapper C# diffuso denominato SQLite-net.
SQLite-net è un mapper relazionale a oggetti. Semplifica il processo di definizione degli schemi di database consentendo di usare i modelli definiti nei progetti come schema.
Si consideri ad esempio la classe seguente che modella un User
:
class User
{
public int Id { get; set; }
public string Username { get; set; }
...
}
Usando un mapper relazionale a oggetti, è possibile accettare questa classe User
iniziale e creare una tabella di database denominata User
con colonne per i campi Id
e Username
in questa classe.
SQLite-net è disponibile come pacchetto NuGet. È necessario aggiungere il pacchetto sqlite-net-pcl alle app per usarlo.
Come connettersi a un database SQLite
È possibile stabilire una connessione a un database SQLite da un'app tramite un oggetto SQLiteConnection
. Questa classe viene definita nello spazio dei nomi SQLite
, insieme agli altri tipi e metodi forniti da SQLite. Quando si crea un'istanza di questo oggetto, passare il nome del file di database. Il costruttore apre il file, se esiste, o in caso contrario lo crea.
Il codice seguente visualizza un esempio:
using SQLite;
...
string filename = ...
SQLiteConnection conn = new SQLiteConnection(filename);
Tenere presente che filename
deve puntare a una posizione nella sandbox dell'app.
Come creare una tabella
Ricordare che SQLite-net è un mapper relazionale a oggetti, ovvero lo schema del database può essere creato da classi C#. SQLite-net può creare una tabella di database da una normale classe C#, ma esistono molti attributi che è possibile aggiungere a una classe per fornire altri metadati. Tali metadati consentono a SQLite di applicare funzionalità, come l'univocità, e di applicare vincoli ai dati.
Gli attributi disponibili includono:
Table
: specificare il nome della tabella se si vuole usare un nome diverso da quello della classe.PrimaryKey
: specificare che una colonna è la chiave primaria.AutoIncrement
: specificare che il valore di una colonna deve aumentare automaticamente quando viene inserita una nuova riga.Column
: Specificare il nome di una colonna se si vuole usare un nome diverso da quello della proprietà.MaxLength
: specificare il numero massimo di caratteri che possono essere usati nella colonna.Unique
: specificare che il valore della colonna deve essere univoco tra tutte le altre righe.
Il codice seguente mostra una versione aggiornata della classe User
che applica questi attributi:
[Table("user")]
public class User
{
// PrimaryKey is typically numeric
[PrimaryKey, AutoIncrement, Column("_id")]
public int Id { get; set; }
[MaxLength(250), Unique]
public string Username { get; set; }
...
}
Dopo aver definito la classe C#, chiamare il metodo generico CreateTable
nella classe SQLiteConnection
per generare la tabella nel database. Specificare la classe come parametro di tipo. Ecco un esempio:
SQLiteConnection conn = new SQLiteConnection(filename);
conn.CreateTable<User>();
Se la tabella esiste già nel database, il metodo CreateTable
controlla lo schema per verificare se sono presenti delle modifiche. In caso affermativo, l'operazione tenta di aggiornare lo schema del database.
Come eseguire operazioni di lettura e scrittura di base
Dopo aver creato una tabella, è possibile iniziare a interagire con essa. Per aggiungere una riga, utilizzare il metodo Insert
nell'istanza di SQLiteConnection
e fornire un oggetto del tipo appropriato che contiene i dati da inserire. Il codice seguente illustra come aggiungere una nuova riga alla tabella User
:
public int AddNewUser(User user)
{
int result = conn.Insert(user);
return result;
}
Il metodo Insert
restituisce un int
, che rappresenta il numero di righe inserite nella tabella. In questo caso il numero è uno.
Per recuperare righe da una tabella, utilizzare il metodo Table
. Questo metodo restituisce una raccolta di oggetti (che potrebbero essere vuoti):
public List<User> GetAllUsers()
{
List<User> users = conn.Table<User>().ToList();
return users;
}
Il metodo Table
restituisce un oggetto TableQuery\<T>
. Per ottenere un List
, usare il metodo ToList
come illustrato nell'esempio precedente.
Eseguire una query SQLite usando LINQ
Il metodo Table
recupera tutte le righe da una tabella. Nella maggior parte dei casi si vuole restituire solo un subset delle righe che corrispondono a un set di criteri specificati. Per queste attività, usare LINQ con SQLite-net.
SQLite-net supporta molte query LINQ comuni tra cui:
Where
Take
Skip
OrderBy
OrderByDescending
ThenBy
ElementAt
First
FirstOrDefault
ThenByDescending
Count
Con questi metodi, è possibile usare la sintassi del metodo di estensione o la sintassi C# LINQ. Ecco ad esempio un frammento di codice che consente di recuperare i dettagli di un utente specificato:
public User GetByUsername(string username)
{
var user = from u in conn.Table<User>()
where u.Username == username
select u;
return user.FirstOrDefault();
}
Aggiornare ed eliminare righe
Aggiornare una riga usando il metodo Update
dell'oggetto SQLiteConnection
. Si fornisce un oggetto che definisce la riga da aggiornare con nuovi valori. Il metodo Update
modifica la riga con lo stesso valore della chiave primaria dell'oggetto fornito. Il valore restituito è il numero di righe modificate. Se questo valore è zero, non sono state trovate righe con una chiave primaria corrispondente e non è stato eseguito alcun aggiornamento. Il frammento successivo mostra questo metodo in azione:
public int UpdateUser(User user)
{
int result = 0;
result = conn.Update(user);
return result;
}
Rimuovere righe da una tabella con il metodo Delete
dell'oggetto SQLiteConnection
. La forma più semplice di questo metodo accetta la chiave primaria dell'elemento da eliminare come parametro, come illustrato nell'esempio seguente. Questo formato del metodo Delete
è generico e richiede un parametro di tipo. Il valore restituito è il numero di righe rimosse dalla tabella:
public int DeleteUser(int userID)
{
int result = 0;
result = conn.Delete<User>(userID);
return result;
}