Condividi tramite


Usare un database SQLite in un'app di Windows

SQLite offre una soluzione di database affidabile e leggera per l'archiviazione dei dati in locale nelle app di Windows. A differenza dei sistemi di database tradizionali che richiedono installazioni server separate e configurazioni complesse, SQLite viene eseguito interamente all'interno del processo dell'applicazione e archivia i dati in un singolo file nel dispositivo dell'utente.

Questa esercitazione illustra come integrare SQLite nell'applicazione WinUI usando le librerie di accesso ai dati consigliate di Microsoft. Si apprenderà come configurare un database, creare tabelle e implementare operazioni di base sui dati, seguendo le migliori pratiche di sicurezza per proteggere da vulnerabilità comuni.

Obiettivi

In questa esercitazione si apprenderà come:

  • Configurare l'app di Windows per l'uso di SQLite con la libreria Microsoft.Data.SQLite
  • Creare e inizializzare un database locale
  • Implementare metodi sicuri per l'inserimento e il recupero dei dati
  • Creare un'interfaccia utente semplice per interagire con i dati

Prerequisiti

Per completare questa esercitazione, è necessario:

  • Visual Studio 2022 o versione successiva con il carico di lavoro sviluppo di applicazioni WinUI
  • Conoscenza di base di C# e XAML
  • Conoscenza dei concetti fondamentali del database

Miglioramenti principali offerti da questo approccio

L'uso di SQLite per l'archiviazione dati locale nell'app di Windows offre diversi vantaggi:

  • Distribuzione semplificata: non è necessaria alcuna installazione separata del server di database
  • Sicurezza avanzata: i dati rimangono locali nel dispositivo dell'utente
  • Prestazioni migliorate: l'accesso diretto ai file elimina la latenza di rete
  • Complessità ridotta: il database a file singolo semplifica il backup e la migrazione

Le tecniche apprese si applicano a qualsiasi app di Windows che deve archiviare i dati strutturati in locale, dall'archiviazione di impostazioni semplici agli scenari di gestione dei dati complessi.

Suggerimento

È possibile usare l'assistenza di intelligenza artificiale per evitare attacchi SQL injection in SQLite.

Vantaggi di SQLite per l'archiviazione locale

✔️ SQLite è leggero e autosufficiente. È una libreria di codice senza altre dipendenze. Non richiede alcuna configurazione.

✔️ Nessun server di database. Il client e il server vengono eseguiti nello stesso processo.

✔️ SQLite è di dominio pubblico e può essere usato e distribuito liberamente con la propria app.

✔️ SQLite funziona su tutte le piattaforme e architetture.

Altre informazioni su SQLite sono disponibili qui.

Scegliere un livello di astrazione

È consigliabile usare Entity Framework Core o la libreria SQLite open source creata da Microsoft.

Entity Framework Core

Entity Framework (EF) è un mapper relazionale a oggetti che ti consente di gestire dati relazionali usando oggetti specifici del dominio. Se questo framework è già stato usato per usare i dati in altre app .NET, è possibile usare lo stesso codice nell'app WinUI e usare le modifiche appropriate alla stringa di connessione.

Per provare, vedere Introduzione a EF Core.

Libreria SQLite

La libreria Microsoft.Data.Sqlite implementa le interfacce nello spazio dei nomi System.Data.Common. Microsoft gestisce attivamente queste implementazioni, che offrono un wrapper intuitivo per l'API SQLite nativa di basso livello.

Il resto di questa guida ti mostra come usare questa libreria.

Configurare la soluzione per l'uso della libreria Microsoft.Data.SQLite

Si inizierà con un progetto WinUI di base e quindi si installerà il pacchetto NuGet SQLite. Vedi Creare un'app WinUI per istruzioni su come creare il primo progetto WinUI.

Tutte le versioni supportate di Windows supportano SQLite, per cui non è necessario che l'app crei pacchetti di librerie SQLite. L'app potrà invece usare la versione di SQLite installata con Windows. Ciò presenta vari vantaggi.

✔️ Riduce le dimensioni dell'applicazione, poiché non è necessario scaricare il file binario di SQLite e creare il pacchetto come parte dell'applicazione.

✔️ Consente di evitare di eseguire il push di una nuova versione dell’app agli utenti nel caso in cui SQLite pubblichi correzioni critiche di bug e vulnerabilità di sicurezza in SQLite. La versione Windows di SQLite è gestita da Microsoft in collaborazione con SQLite.org.

✔️ Il tempo di caricamento dell'app è potenzialmente più veloce perché molto probabilmente la versione SDK di SQLite sarà già caricata in memoria.

Per iniziare, aggiungere al progetto una classe denominata DataAccess. Se si prevede di condividere la logica di accesso ai dati con altro codice client, è possibile usare un progetto di libreria di classi .NET per contenere il codice di accesso ai dati, ma nell’esempio non ne useremo uno.

  1. Fai clic con il pulsante destro del mouse sulla soluzione e quindi scegli Gestisci pacchetti NuGet per la soluzione.

    Screenshot di Esplora soluzioni di Visual Studio con il progetto su cui è stato fatto clic con il pulsante destro del mouse e l'opzione Gestisci pacchetti NuGet evidenziata.

    A questo punto puoi procedere in due modi. Puoi usare la versione di SQLite inclusa in Windows oppure, se per qualche motivo è opportuno usare una versione specifica di SQLite, puoi includere la libreria SQLite nel pacchetto. Useremo la versione di SQLite inclusa in Windows.

  2. Scegli la scheda Browse , cerca il pacchetto Microsoft.Data.SQLite e installa l'ultima versione stabile.

    Pacchetto NuGet SQLite

Aggiungere e recuperare dati in un database SQLite

Eseguiremo queste operazioni:

1️⃣ Preparare la classe di accesso ai dati.

2️⃣ Inizializzare il database SQLite.

3️⃣ Inserire i dati nel database SQLite.

4️⃣ Recuperare i dati dal database SQLite.

5️⃣ Aggiungere un'interfaccia utente di base.

Preparare la classe di accesso ai dati

Aprire la classe DataAccess nel progetto e renderla statica.

Nota

Mentre l'esempio inserisce il codice di accesso ai dati in una classe statica, si tratta di una scelta di progettazione ed è completamente facoltativo.

public static class DataAccess
{
}

Aggiungere le istruzioni using seguenti all'inizio del file.

using Microsoft.Data.Sqlite;
using System.Collections.Generic;

Inizializzare il database SQLite

Aggiungere un metodo alla classe DataAccess che inizializzi il database SQLite.

public async static void InitializeDatabase()
{ 
    await ApplicationData.Current.LocalFolder
            .CreateFileAsync("sqliteSample.db", CreationCollisionOption.OpenIfExists);
    string dbpath = Path.Combine(ApplicationData.Current.LocalFolder.Path,
                                 "sqliteSample.db");
    using (var db = new SqliteConnection($"Filename={dbpath}"))
    {
        db.Open();

        string tableCommand = "CREATE TABLE IF NOT " +
            "EXISTS MyTable (Primary_Key INTEGER PRIMARY KEY, " +
            "Text_Entry NVARCHAR(2048) NULL)";

        var createTable = new SqliteCommand(tableCommand, db);

        createTable.ExecuteReader();
    }
}

Nota

Il codice precedente, che usa ApplicationData membri, funziona solo per le app in pacchetto eseguite in un contenitore di app. Tutte le altre app di Windows devono accedere a ApplicationData membri tramite la classe ApplicationDataManager.

Questo codice crea il database SQLite e lo archivia nell'archivio dati locali dell'applicazione.

In questo esempio denominiamo il database sqlliteSample.db, ma puoi scegliere qualsiasi altro nome a condizione che tale nome venga usato in tutti gli oggetti SqliteConnection di cui crei un'istanza. In un'applicazione di produzione, le informazioni di connessione, come il nome del file del database, devono essere archiviate nella configurazione dell'app anziché hardcoded (vedere Aggiunta di Configurazione app di Azure tramite Servizi connessi di Visual Studio).

Nel costruttore del file App.xaml.cs del progetto, chiamare il metodo InitializeDatabase della classe DataAccess. In questo modo si garantisce che il database venga creato o aperto a ogni avvio dell'app.

public App()
{
    this.InitializeComponent();

    DataAccess.InitializeDatabase();
}

Inserire dati nel database SQLite

Aggiungere un metodo alla classe DataAccess che inserisca i dati nel database SQLite. Questo codice usa i parametri della query per impedire attacchi SQL injection.

public static void AddData(string inputText)
{
    string dbpath = Path.Combine(ApplicationData.Current.LocalFolder.Path,
                                 "sqliteSample.db");
    using (var db = new SqliteConnection($"Filename={dbpath}"))
    {
        db.Open();

        var insertCommand = new SqliteCommand();
        insertCommand.Connection = db;

        // Use parameterized query to prevent SQL injection attacks
        insertCommand.CommandText = "INSERT INTO MyTable VALUES (NULL, @Entry);";
        insertCommand.Parameters.AddWithValue("@Entry", inputText);

        insertCommand.ExecuteReader();
    }

}

Recuperare dati dal database SQLite

Aggiungere un metodo che ottenga tutte le righe di dati da una tabella del database SQLite.

public static List<string> GetData()
{
    var entries = new List<string>();
    string dbpath = Path.Combine(ApplicationData.Current.LocalFolder.Path,
                                 "sqliteSample.db");
    using (var db = new SqliteConnection($"Filename={dbpath}"))
    {
        db.Open();
        var selectCommand = new SqliteCommand
            ("SELECT Text_Entry from MyTable", db);

        SqliteDataReader query = selectCommand.ExecuteReader();

        while (query.Read())
        {
            entries.Add(query.GetString(0));
        }
    }

    return entries;
}

Il metodo Read procede scorrendo le righe dei dati restituiti. Restituisce true se rimangono delle righe, altrimenti restituisce false.

Il metodo GetString restituisce il valore della colonna specificata come stringa. Il metodo accetta un valore intero che rappresenta l'ordinale di colonna in base zero dei dati desiderati. Puoi usare metodi simili, ad esempio GetDataTime e GetBoolean. Scegli un metodo in base al tipo di dati contenuto nella colonna.

Il parametro dell'ordinale non è importante in questo esempio perché selezioniamo tutte le voci di una singola colonna. Se la query include più colonne, tuttavia, usa il valore dell'ordinale per ottenere la colonna da cui vuoi recuperare i dati.

Aggiungere un'interfaccia utente di base

Nel file MainWindow.xaml del progetto, aggiungere il codice XAML seguente.

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel>
        <TextBox x:Name="Input_Box"/>
        <Button Click="AddData">Add</Button>
        <ListView x:Name="Output">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding}"/>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackPanel>
</Grid>

Questa interfaccia utente di base offre un controllo TextBox che l'utente può usare per digitare una stringa che verrà aggiunta al database SQLite. Connetteremo il controllo Button dell'interfaccia utente a un gestore eventi che recupererà i dati dal database SQLite e li visualizzerà in ListView.

Nel file MainWindow.xaml.cs aggiungere il gestore seguente. Questo è il metodo che abbiamo associato all'evento Click di Button nell'interfaccia utente.

private void AddData(object sender, RoutedEventArgs e)
{
    DataAccess.AddData(Input_Box.Text);

    Output.ItemsSource = DataAccess.GetData();
}

Si vuole essere certi che i dati esistenti vengano caricati all'avvio dell'applicazione. Aggiungere una riga di codice al costruttore MainWindow per chiamare GetData().

public MainWindow()
{
    this.InitializeComponent();

    Output.ItemsSource = DataAccess.GetData();
}

È tutto. Esplora Microsoft.Data.Sqlite per scoprire cos'altro puoi fare con il database SQLite. Consultate i collegamenti seguenti per altre informazioni sulle modalità di utilizzo dei dati nelle app di Windows.

Evitare attacchi di SQL injection

Il codice in questo esempio usa query con parametri per evitare attacchi SQL injection. Non concatenare mai l'input dell'utente in una stringa di query SQL. Usare sempre i parametri. È possibile chiedere a Copilot altri suggerimenti per evitare attacchi SQL injection.

Il testo seguente mostra un esempio di richiesta di Copilot:

Can you provide some best practices to avoid SQL injection attacks when writing SQLite queries in C# code?

Copilot è alimentato dall'IA, quindi sono possibili sorprese ed errori. Per altre informazioni, vedere domande frequenti su Copilot.

Connettere l'app direttamente a un database di SQL Server

Vedere Usare un database di SQL Server in un'app di Windows.

Condividere codice tra app diverse in piattaforme diverse

Vedi Condividere codice tra un'app desktop e un'app UWP.

Aggiungere pagine master/dettagli con back-end SQL di Azure

Vedi l'esempio di database per gli ordini dei clienti.