Udostępnij za pośrednictwem


Lokalne bazy danych programu .NET MAUI

Przeglądaj przykład. Przeglądanie przykładu

Aparat bazy danych SQLite umożliwia aplikacjom interfejsu użytkownika aplikacji wieloplatformowych platformy .NET (.NET MAUI) ładowanie i zapisywanie obiektów danych w kodzie udostępnionym. Możesz zintegrować SQLite.NET z aplikacjami .NET MAUI, aby przechowywać i pobierać informacje w lokalnej bazie danych, wykonując następujące kroki:

  1. Zainstaluj pakiet NuGet.
  2. Konfigurowanie stałych.
  3. Utwórz klasę dostępu do bazy danych.
  4. Uzyskiwanie dostępu do danych.
  5. Konfiguracja zaawansowana.

W tym artykule użyto pakietu NuGet sqlite-net-pcl w celu zapewnienia dostępu bazy danych SQLite do tabeli do przechowywania zadań do wykonania. Alternatywą jest użycie pakietu NuGet Microsoft.Data.Sqlite , który jest uproszczonym dostawcą ADO.NET dla biblioteki SQLite. Microsoft.Data.Sqlite implementuje typowe abstrakcje ADO.NET dla funkcji, takich jak połączenia, polecenia i czytniki danych.

Instalowanie pakietu NuGet SQLite

Użyj menedżera pakietów NuGet, aby wyszukać pakiet sqlite-net-pcl i dodać najnowszą wersję do projektu aplikacji .NET MAUI.

Istnieje kilka pakietów NuGet o podobnych nazwach. Poprawny pakiet ma następujące atrybuty:

  • Identyfikator: sqlite-net-pcl
  • Autorzy: SQLite-net
  • Właściciele: praeclarum
  • Link NuGet: sqlite-net-pcl

Pomimo nazwy pakietu użyj pakietu NuGet sqlite-net-pcl w projektach .NET MAUI.

Ważne

SQLite.NET to biblioteka innej firmy obsługiwana przez repozytorium praeclarum/sqlite-net.

Instalowanie SQLitePCLRaw.bundle_green

Oprócz sqlite-net-pcl tymczasowo należy zainstalować podstawową zależność, która uwidacznia sqlite na każdej platformie:

  • Identyfikator: SQLitePCLRaw.bundle_green
  • Wersja:>= 2.1.0
  • Autorzy: Eric Sink
  • Właściciele: Eric Sink
  • Link narzędzia NuGet: SQLitePCLRaw.bundle_green

Konfigurowanie stałych aplikacji

Dane konfiguracji, takie jak nazwa pliku bazy danych i ścieżka, mogą być przechowywane jako stałe w aplikacji. Przykładowy projekt zawiera plik Constants.cs zawierający typowe dane konfiguracji:

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);
}

W tym przykładzie plik stałych określa domyślne SQLiteOpenFlag wartości wyliczenia, które są używane do inicjowania połączenia z bazą danych. Wyliczenie SQLiteOpenFlag obsługuje następujące wartości:

  • Create: Połączenie automatycznie utworzy plik bazy danych, jeśli nie istnieje.
  • FullMutex: Połączenie jest otwierane w trybie serializacji wątków.
  • NoMutex: Połączenie jest otwierane w trybie wielowątkowy.
  • PrivateCache: Połączenie nie będzie uczestniczyć w udostępnionej pamięci podręcznej, nawet jeśli jest włączone.
  • ReadWrite: Połączenie może odczytywać i zapisywać dane.
  • SharedCache: Połączenie będzie uczestniczyć w udostępnionej pamięci podręcznej, jeśli jest włączone.
  • ProtectionComplete: plik jest zaszyfrowany i niedostępny, gdy urządzenie jest zablokowane.
  • ProtectionCompleteUnlessOpen: plik jest szyfrowany, dopóki nie zostanie otwarty, ale będzie dostępny nawet wtedy, gdy użytkownik zablokuje urządzenie.
  • ProtectionCompleteUntilFirstUserAuthentication: plik jest szyfrowany do momentu uruchomienia i odblokowania urządzenia przez użytkownika.
  • ProtectionNone: plik bazy danych nie jest zaszyfrowany.

Może być konieczne określenie różnych flag w zależności od sposobu użycia bazy danych. Aby uzyskać więcej informacji na temat SQLiteOpenFlagsprogramu , zobacz Otwieranie nowego połączenia z bazą danych na sqlite.org.

Tworzenie klasy dostępu do bazy danych

Klasa otoki bazy danych abstrakcji warstwy dostępu do danych z pozostałej części aplikacji. Ta klasa centralizuje logikę zapytań i upraszcza zarządzanie inicjowaniem bazy danych, co ułatwia refaktoryzowanie lub rozszerzanie operacji na danych w miarę rozwoju aplikacji. Przykładowa aplikacja definiuje klasę TodoItemDatabase w tym celu.

Inicjowanie z opóźnieniem

Metoda TodoItemDatabase używa asynchronicznej leniwej inicjalizacji w celu opóźnienia inicjowania bazy danych do momentu pierwszego uzyskania do niej dostępu z prostą Init metodą wywoływaną przez każdą metodę w klasie:

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>();
    }
    ...
}

Metody manipulowania danymi

Klasa TodoItemDatabase zawiera metody dla czterech typów manipulowania danymi: tworzenie, odczytywanie, edytowanie i usuwanie. Biblioteka SQLite.NET udostępnia prostą relacyjną mapę obiektów (ORM), która umożliwia przechowywanie i pobieranie obiektów bez konieczności pisania instrukcji SQL.

W poniższym przykładzie przedstawiono metody manipulowania danymi w przykładowej aplikacji:

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);
    }
}

Uzyskiwanie dostępu do danych

Klasę TodoItemDatabase można zarejestrować jako pojedynczą, która może być używana w całej aplikacji, jeśli używasz iniekcji zależności. Możesz na przykład zarejestrować strony i klasę dostępu bazy danych jako usługi w IServiceCollection obiekcie w MauiProgram.cs za pomocą AddSingleton metod i AddTransient :

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

builder.Services.AddSingleton<TodoItemDatabase>();

Te usługi można następnie automatycznie wprowadzać do konstruktorów klas i uzyskiwać do nich dostęp:

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("..");
}

Alternatywnie można utworzyć nowe wystąpienia klasy dostępu bazy danych:

TodoItemDatabase database;

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

Aby uzyskać więcej informacji na temat wstrzykiwania zależności w aplikacjach .NET MAUI, zobacz Wstrzykiwanie zależności.

Konfiguracja zaawansowana

SqLite udostępnia niezawodny interfejs API z większą ilością funkcji, niż opisano w tym artykule i przykładowej aplikacji. W poniższych sekcjach opisano funkcje, które są ważne dla skalowalności.

Aby uzyskać więcej informacji, zobacz dokumentację sqlite dotyczącą sqlite.org.

Rejestrowanie z wyprzedzeniem zapisu

Domyślnie sqLite używa tradycyjnego dziennika wycofywania. Kopia niezmienionej zawartości bazy danych jest zapisywana w osobnym pliku wycofywania, a następnie zmiany są zapisywane bezpośrednio w pliku bazy danych. Zatwierdzenie występuje po usunięciu dziennika wycofywania.

Funkcja write-Ahead Logging (WAL) zapisuje zmiany w osobnym pliku WAL najpierw. W trybie WAL zatwierdzenie jest specjalnym rekordem dołączonym do pliku WAL, który umożliwia wykonywanie wielu transakcji w jednym pliku WAL. Plik WAL jest scalony z powrotem do pliku bazy danych w specjalnej operacji nazywanej punktem kontrolnym.

Funkcja WAL może być szybsza w przypadku lokalnych baz danych, ponieważ czytelnicy i autorzy nie blokują się nawzajem, co pozwala na współbieżne operacje odczytu i zapisu. Jednak tryb WAL nie zezwala na zmiany rozmiaru strony, dodaje dodatkowe skojarzenia plików do bazy danych i dodaje dodatkową operację tworzenia punktów kontrolnych.

Aby włączyć funkcję WAL w SQLite.NET, wywołaj metodę EnableWriteAheadLoggingAsync w wystąpieniu SQLiteAsyncConnection :

await Database.EnableWriteAheadLoggingAsync();

Aby uzyskać więcej informacji, zobacz SQLite Write-Ahead Logging on sqlite.org (Rejestrowanie przed zapisem sqlite na sqlite.org).

Kopiowanie bazy danych

Istnieje kilka przypadków, w których może być konieczne skopiowanie bazy danych SQLite:

  • Baza danych została dostarczona z aplikacją, ale musi zostać skopiowana lub przeniesiona do magazynu zapisywalnego na urządzeniu przenośnym.
  • Należy utworzyć kopię zapasową lub kopię bazy danych.
  • Musisz wersję, przenieść lub zmienić nazwę pliku bazy danych.

Ogólnie rzecz biorąc, przenoszenie, zmienianie nazwy lub kopiowanie pliku bazy danych jest tym samym procesem co każdy inny typ pliku z kilkoma dodatkowymi zagadnieniami:

  • Przed podjęciem próby przeniesienia pliku bazy danych należy zamknąć wszystkie połączenia bazy danych.
  • Jeśli używasz funkcji Write-Ahead Logging, narzędzie SQLite utworzy plik dostępu do pamięci udostępnionej (shm) i plik (Write Ahead Log) (wal). Upewnij się, że zastosowano również wszelkie zmiany w tych plikach.