Bases de données locales .NET MAUI

Browse sample. Parcourir l’exemple

Le moteur de base de données SQLite permet aux applications .NET Multiplateforme App UI (.NET MAUI) de charger et d’enregistrer des objets de données dans du code partagé. Vous pouvez intégrer SQLite.NET aux applications .NET MAUI pour stocker et récupérer des informations dans une base de données locale en procédant comme suit :

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

Cet article utilise le package NuGet sqlite-net-pcl pour fournir l’accès à une base de données SQLite à une table pour stocker des éléments todo. Une alternative consiste à utiliser le package NuGet Microsoft.Data.Sqlite, qui est un fournisseur de ADO.NET léger pour SQLite. Microsoft.Data.Sqlite implémente les abstractions de ADO.NET courantes pour les 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 ajouter la dernière version à 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 du package, utilisez le package NuGet sqlite-net-pcl dans les projets .NET MAUI.

Important

SQLite.NET est une bibliothèque tierce prise en charge à partir du dépôt 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 nom de fichier et le chemin d’accès de la base de données, peuvent être stockées sous forme de 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 de constantes spécifie les valeurs d’énumération par défaut SQLiteOpenFlag 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 threading sérialisé.
  • NoMutex: la connexion est ouverte en mode multithreading.
  • PrivateCache: la connexion ne participe pas au cache partagé, même si elle est activée.
  • ReadWrite: la connexion peut lire et écrire des données.
  • SharedCache: la connexion participe au 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’à ce qu’il soit ouvert, 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 votre base de données sera utilisée. Pour plus d’informations sur SQLiteOpenFlags, consultez Ouverture d’une nouvelle base de données Connecter ion sur sqlite.org.

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

Une classe wrapper de base de données extrait 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 base de données, ce qui facilite la refactorisation ou l’expansion des opérations de données à mesure que l’application augmente. L’exemple d’application définit une TodoItemDatabase classe à cet effet.

Initialisation différée

Il TodoItemDatabase utilise l’initialisation différée asynchrone pour retarder l’initialisation de la base de données jusqu’à ce qu’elle soit utilisée pour la première fois, avec une méthode simple Init qui est appelée par chaque méthode de 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 TodoItemDatabase classe 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 relationnelle d’objet simple (ORM) 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 des 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

La TodoItemDatabase classe peut être inscrite en tant que singleton qui peut être utilisé dans l’ensemble de l’application si vous utilisez l’injection de dépendances. Par exemple, vous pouvez inscrire vos pages et la classe d’accès à la base de données en tant que services sur l’objetIServiceCollection, dans MauiProgram.cs, avec les méthodes et AddTransient les AddSingleton méthodes :

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

builder.Services.AddSingleton<TodoItemDatabase>();

Ces services peuvent ensuite être injectés automatiquement dans des constructeurs de classe et accessibles :

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 de nouvelles 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 l’injection de dépendances.

Configuration avancée

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

Pour plus d’informations, consultez la documentation SQLite sur sqlite.org.

Journalisation en écriture anticipée

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, puis les modifications sont écrites directement dans le fichier de base de données. La validation se produit lorsque le journal de restauration est supprimé.

La journalisation en écriture anticipée (WAL) é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 case activée point.

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 de lecture et d’é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 de case activée point supplémentaire.

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

await Database.EnableWriteAheadLoggingAsync();

Pour plus d’informations, consultez la journalisation sqLite write-ahead 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 mettre en 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 :

  • Toutes les connexions de base de données doivent être fermées avant de tenter de déplacer le fichier de base de données.
  • Si vous utilisez la journalisation write-ahead, 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.