Partager via


Bases de données .NET MAUI locales

Parcourez l’exemple. Parcourir l'exemple

Le moteur de base de données SQLite permet aux applications du kit .NET Multi-Platform App UI (.NET MAUI) de charger et d’enregistrer des objets de données dans du code partagé. Vous pouvez intégrer SQLite.NET dans des applications .NET MAUI pour stocker et récupérer des informations dans une base de données locale en suivant ces étapes :

  1. Installez le package NuGet.
  2. Configurez des constantes.
  3. Créez une classe d’accès à la base de données.
  4. Accédez aux données.
  5. Configuration avancée.

Cet article utilise le package NuGet sqlite-net-pcl pour fournir l’accès à la base de données SQLite à une table pour stocker des éléments de liste de tâches. Une autre solution consiste à utiliser le package NuGet Microsoft-Data-Sqlite qui est un fournisseur ADO.NET léger pour SQLite. Microsoft.Data.Sqlite implémente les abstractions ADO.NET courantes pour des fonctionnalités telles que les connexions, les commandes et les lecteurs de données.

Installer le package NuGet SQLite

Utilisez le gestionnaire de package NuGet pour rechercher le package sqlite-net-pcl et ajoutez la dernière version dans votre projet d’application .NET MAUI.

Il existe plusieurs packages NuGet portant des noms similaires. Le package correct possède ces attributs :

  • ID : sqlite-net-pcl
  • Auteurs : SQLite-net
  • Propriétaires : praeclarum
  • Lien NuGet : sqlite-net-pcl

Malgré le nom de package, utilisez le package sqlite-net-pcl NuGet dans des projets .NET MAUI.

Important

SQLite.NET est une bibliothèque tierce prise en charge à partir du référentiel praeclarum/sqlite-net.

Installer SQLitePCLRaw.bundle_green

Outre sqlite-net-pcl, vous devez installer temporairement la dépendance sous-jacente qui expose SQLite sur chaque plateforme :

  • ID : SQLitePCLRaw.bundle_green
  • Version :>= 2.1.0
  • Auteurs : Eric Sink
  • Propriétaires : Eric Sink
  • Lien NuGet : SQLitePCLRaw.bundle_green

Configurer des constantes d’application

Les données de configuration, telles que le chemin d’accès et le nom du fichier de base de données, peuvent être stockées comme constantes dans votre application. L’exemple de projet inclut un fichier Constants.cs qui fournit des données de configuration courantes :

public static class Constants
{
    public const string DatabaseFilename = "TodoSQLite.db3";

    public const SQLite.SQLiteOpenFlags Flags =
        // open the database in read/write mode
        SQLite.SQLiteOpenFlags.ReadWrite |
        // create the database if it doesn't exist
        SQLite.SQLiteOpenFlags.Create |
        // enable multi-threaded database access
        SQLite.SQLiteOpenFlags.SharedCache;

    public static string DatabasePath =>
        Path.Combine(FileSystem.AppDataDirectory, DatabaseFilename);
}

Dans cet exemple, le fichier des constantes spécifie les valeurs d’énumération SQLiteOpenFlag par défaut utilisées pour initialiser la connexion de base de données. L’énumération SQLiteOpenFlag prend en charge ces valeurs :

  • Create : La connexion crée automatiquement le fichier de base de données s’il n’existe pas.
  • FullMutex : La connexion est ouverte en mode de thread sérialisé.
  • NoMutex : La connexion est ouverte en mode multithreading.
  • PrivateCache : La connexion ne participe pas dans le cache partagé, même si elle est activée.
  • ReadWrite : La connexion peut lire et écrire des données.
  • SharedCache : La connexion participe dans le cache partagé si elle est activée.
  • ProtectionComplete : Le fichier est chiffré et inaccessible pendant que l’appareil est verrouillé.
  • ProtectionCompleteUnlessOpen : Le fichier est chiffré jusqu’à son ouverture, mais il est alors accessible même si l’utilisateur verrouille l’appareil.
  • ProtectionCompleteUntilFirstUserAuthentication : Le fichier est chiffré jusqu’à ce que l’utilisateur ait démarré et déverrouillé l’appareil.
  • ProtectionNone : Le fichier de base de données n’est pas chiffré.

Vous devrez peut-être spécifier différents indicateurs en fonction de la façon dont vous utiliserez votre base de données. Pour obtenir plus d’informations sur SQLiteOpenFlags, consultez Ouverture d’une nouvelle connexion de base de données sur sqlite.org.

Créer une classe d’accès à la base de données

Une classe wrapper de base de données abstrait la couche d’accès aux données à partir du reste de l’application. Cette classe centralise la logique de requête et simplifie la gestion de l’initialisation de la base de données, ce qui facilite la refactorisation ou l’expansion des opérations de données au fur et à mesure du développement de l’application. L’exemple d’application définit une classe TodoItemDatabase à cette fin.

Initialisation tardive

La TodoItemDatabase utilise une initialisation tardive asynchrone pour retarder l’initialisation d’une base de données jusqu’à son premier accès avec une méthode Init simple appelée par chaque méthode dans la classe :

public class TodoItemDatabase
{
    SQLiteAsyncConnection Database;

    public TodoItemDatabase()
    {
    }

    async Task Init()
    {
        if (Database is not null)
            return;

        Database = new SQLiteAsyncConnection(Constants.DatabasePath, Constants.Flags);
        var result = await Database.CreateTableAsync<TodoItem>();
    }
    ...
}

Méthodes de manipulation des données

La classe TodoItemDatabase inclut des méthodes pour les quatre types de manipulation de données : créer, lire, modifier et supprimer. La bibliothèque SQLite.NET fournit une carte objet-relationnel (ORM) simple qui vous permet de stocker et de récupérer des objets sans écrire d’instructions SQL.

L’exemple suivant montre les méthodes de manipulation de données dans l’exemple d’application :

public class TodoItemDatabase
{
    ...
    public async Task<List<TodoItem>> GetItemsAsync()
    {
        await Init();
        return await Database.Table<TodoItem>().ToListAsync();
    }

    public async Task<List<TodoItem>> GetItemsNotDoneAsync()
    {
        await Init();
        return await Database.Table<TodoItem>().Where(t => t.Done).ToListAsync();

        // SQL queries are also possible
        //return await Database.QueryAsync<TodoItem>("SELECT * FROM [TodoItem] WHERE [Done] = 0");
    }

    public async Task<TodoItem> GetItemAsync(int id)
    {
        await Init();
        return await Database.Table<TodoItem>().Where(i => i.ID == id).FirstOrDefaultAsync();
    }

    public async Task<int> SaveItemAsync(TodoItem item)
    {
        await Init();
        if (item.ID != 0)
            return await Database.UpdateAsync(item);
        else
            return await Database.InsertAsync(item);
    }

    public async Task<int> DeleteItemAsync(TodoItem item)
    {
        await Init();
        return await Database.DeleteAsync(item);
    }
}

Accéder aux données

Vous pouvez enregistrer la classe TodoItemDatabase comme singleton qui peut être utilisé dans l’application si vous utilisez une injection de dépendance. Par exemple, vous pouvez inscrire vos pages et la classe d’accès à la base de données comme services sur l’objet IServiceCollection dans MauiProgram.cs avec les méthodes AddSingleton et AddTransient :

builder.Services.AddSingleton<TodoListPage>();
builder.Services.AddTransient<TodoItemPage>();

builder.Services.AddSingleton<TodoItemDatabase>();

Vous pouvez ensuite automatiquement injecter ces services dans des constructeurs de classe et y accéder :

TodoItemDatabase database;

public TodoItemPage(TodoItemDatabase todoItemDatabase)
{
    InitializeComponent();
    database = todoItemDatabase;
}

async void OnSaveClicked(object sender, EventArgs e)
{
    if (string.IsNullOrWhiteSpace(Item.Name))
    {
        await DisplayAlert("Name Required", "Please enter a name for the todo item.", "OK");
        return;
    }

    await database.SaveItemAsync(Item);
    await Shell.Current.GoToAsync("..");
}

Vous pouvez également créer des instances de la classe d’accès à la base de données :

TodoItemDatabase database;

public TodoItemPage()
{
    InitializeComponent();
    database = new TodoItemDatabase();
}

Pour plus d’informations sur l’injection de dépendances dans les applications .NET MAUI, consultez Injection de dépendances.

Configuration avancée

SQLite fournit une API robuste avec davantage de fonctionnalités que ce qui est traité dans cet article et l’exemple d’application. Les sections suivantes abordent les fonctionnalités importantes pour l’extensibilité.

Pour obtenir plus d’informations, voir Documentation SQLite sur sqlite.org.

Journalisation WAL (Write-Ahead Logging)

Par défaut, SQLite utilise un journal de restauration traditionnel. Une copie du contenu de base de données inchangé est écrite dans un fichier de restauration distinct. Les modifications sont ensuite écrites directement dans le fichier de base de données. Le COMMIT se produit lorsque le journal de restauration est supprimé.

La journalisation WAL (Write-Ahead Logging) écrit d’abord les modifications dans un fichier WAL distinct. En mode WAL, un COMMIT est un enregistrement spécial, ajouté au fichier WAL, qui permet à plusieurs transactions de se produire dans un seul fichier WAL. Un fichier WAL est fusionné dans le fichier de base de données dans une opération spéciale appelée point de contrôle.

WAL peut être plus rapide pour les bases de données locales, car les lecteurs et les enregistreurs ne se bloquent pas mutuellement, ce qui permet aux opérations en lecture et en écriture d’être simultanées. Toutefois, le mode WAL n’autorise pas les modifications apportées à la taille de page, ajoute des associations de fichiers supplémentaires à la base de données et ajoute l’opération supplémentaire de points de contrôle.

Pour activer WAL dans SQLite.NET, appelez la méthode EnableWriteAheadLoggingAsync sur l’instance SQLiteAsyncConnection :

await Database.EnableWriteAheadLoggingAsync();

Pour obtenir plus d’informations, consultez Write-Ahead Logging de SQLite sur sqlite.org.

Copier une base de données

Il existe plusieurs cas où il peut être nécessaire de copier une base de données SQLite :

  • Une base de données a été fournie avec votre application, mais doit être copiée ou déplacée vers un stockage accessible en écriture sur l’appareil mobile.
  • Vous devez effectuer une sauvegarde ou une copie de la base de données.
  • Vous devez attribuer une version, déplacer ou renommer le fichier de base de données.

En général, le déplacement, le changement de nom ou la copie d’un fichier de base de données est le même processus que tout autre type de fichier avec quelques considérations supplémentaires :

  • Vous devez fermer toutes les connexions de base de données avant de tenter de déplacer le fichier de base de données.
  • Si vous utilisez la journalisation Write-Ahead Logging, SQLite crée un fichier d’accès à la mémoire partagée (.shm) et un fichier (Write Ahead Log) (.wal). Veillez également à appliquer les modifications apportées à ces fichiers.