Xamarin.Forms Lokale Datenbanken

Download Sample Das Beispiel herunterladen

Mit dem SQLite-Datenbankmodul können Xamarin.Forms Anwendungen Datenobjekte im freigegebenen Code laden und speichern. Die Beispielanwendung verwendet eine SQLite-Datenbanktabelle zum Speichern von Todoelementen. In diesem Artikel wird beschrieben, wie Sie SQLite.Net im freigegebenen Code verwenden, um Informationen in einer lokalen Datenbank zu speichern und abzurufen.

Screenshots of the Todolist app on iOS and Android

Integrieren Sie SQLite.NET in mobile Apps, indem Sie die folgenden Schritte ausführen:

  1. Installieren Sie das NuGet-Paket.
  2. Konfigurieren von Konstanten.
  3. Erstellen Sie eine Datenbankzugriffsklasse.
  4. Zugreifen auf Daten in Xamarin.Forms.
  5. Erweiterte Konfiguration.

Installieren des SQLite-NuGet-Pakets

Verwenden Sie den NuGet Paket-Manager, um nach sqlite-net-pcl zu suchen und die neueste Version zum freigegebenen Codeprojekt hinzuzufügen.

Es gibt eine Reihe von NuGet-Paketen mit ähnlichen Namen. Das richtige Paket verfügt über die folgenden Attribute:

  • ID: sqlite-net-pcl
  • Ersteller: SQLite-net
  • Besitzer: praeclarum
  • NuGet-Link:sqlite-net-pcl

Hinweis

Verwenden Sie trotz des Paketnamens in .NET Standard-Projekten das NuGet-Paket sqlite-net-pcl.

Konfigurieren von App-Konstanten

Das Beispielprojekt enthält eine Constants.cs-Datei , die allgemeine Konfigurationsdaten bereitstellt:

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
    {
        get
        {
            var basePath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
            return Path.Combine(basePath, DatabaseFilename);
        }
    }
}

Die Konstantendatei gibt Standardwerte SQLiteOpenFlag an, die zum Initialisieren der Datenbankverbindung verwendet werden. Die SQLiteOpenFlag Aufzählung unterstützt diese Werte:

  • Create: Die Verbindung erstellt automatisch die Datenbankdatei, wenn sie nicht vorhanden ist.
  • FullMutex: Die Verbindung wird im serialisierten Threadingmodus geöffnet.
  • NoMutex: Die Verbindung wird im Multithreadingmodus geöffnet.
  • PrivateCache: Die Verbindung nimmt nicht an dem freigegebenen Cache teil, auch wenn es aktiviert ist.
  • ReadWrite: Die Verbindung kann Daten lesen und schreiben.
  • SharedCache: Die Verbindung nimmt an dem freigegebenen Cache teil, wenn sie aktiviert ist.
  • ProtectionComplete: Die Datei ist verschlüsselt und nicht zugänglich, während das Gerät gesperrt ist.
  • ProtectionCompleteUnlessOpen: Die Datei wird verschlüsselt, bis sie geöffnet ist, aber dann auch dann zugänglich ist, wenn der Benutzer das Gerät sperrt.
  • ProtectionCompleteUntilFirstUserAuthentication: Die Datei wird verschlüsselt, bis der Benutzer gestartet und das Gerät entsperrt hat.
  • ProtectionNone: Die Datenbankdatei ist nicht verschlüsselt.

Möglicherweise müssen Sie verschiedene Flags angeben, je nachdem, wie Ihre Datenbank verwendet wird. Weitere Informationen finden SQLiteOpenFlagsSie unter Öffnen einer neuen Datenbankverbindung auf sqlite.org.

Erstellen einer Datenbankzugriffsklasse

Eine Datenbankumbruchklasse abstrahiert die Datenzugriffsebene aus dem Rest der App. Diese Klasse zentralisiert Abfragelogik und vereinfacht die Verwaltung der Datenbank initialisierung, wodurch es einfacher wird, Datenvorgänge neu zu gestalten oder zu erweitern, wenn die App wächst. Die Todo-App definiert eine TodoItemDatabase Klasse für diesen Zweck.

Verzögerte Initialisierung

Die TodoItemDatabase asynchrone Initialisierung, die durch die benutzerdefinierte AsyncLazy<T> Klasse dargestellt wird, verwendet, um die Initialisierung der Datenbank zu verzögern, bis sie zuerst zugegriffen wird:

public class TodoItemDatabase
{
    static SQLiteAsyncConnection Database;

    public static readonly AsyncLazy<TodoItemDatabase> Instance = new AsyncLazy<TodoItemDatabase>(async () =>
    {
        var instance = new TodoItemDatabase();
        CreateTableResult result = await Database.CreateTableAsync<TodoItem>();
        return instance;
    });

    public TodoItemDatabase()
    {
        Database = new SQLiteAsyncConnection(Constants.DatabasePath, Constants.Flags);
    }

    //...
}

Das Instance Feld wird verwendet, um die Datenbanktabelle für das TodoItem Objekt zu erstellen, wenn es noch nicht vorhanden ist, und gibt einen TodoItemDatabase Singleton zurück. Das Instance Feld des Typs AsyncLazy<TodoItemDatabase> wird zum ersten Mal erstellt, wenn er erwartet wird. Wenn mehrere Threads versuchen, gleichzeitig auf das Feld zuzugreifen, verwenden sie alle die einzelne Konstruktion. Wenn die Konstruktion abgeschlossen ist, werden alle await Vorgänge abgeschlossen. Darüber hinaus werden alle await Vorgänge nach Abschluss des Baus sofort fortgesetzt, da der Wert verfügbar ist.

Hinweis

Die Datenbankverbindung ist ein statisches Feld, das sichergestellt, dass eine einzelne Datenbankverbindung für die Lebensdauer der App verwendet wird. Die Verwendung einer beständigen, statischen Verbindung bietet eine bessere Leistung als das Öffnen und Schließen von Verbindungen während einer einzelnen App-Sitzung.

Asynchrone Initialisierung

Um die Datenbankinitialisierung zu starten, die Ausführung zu vermeiden und Ausnahmen zu erfassen, verwendet die Beispielanwendung asynchrone Initalisierung, dargestellt durch die AsyncLazy<T> Klasse:

public class AsyncLazy<T>
{
    readonly Lazy<Task<T>> instance;

    public AsyncLazy(Func<T> factory)
    {
        instance = new Lazy<Task<T>>(() => Task.Run(factory));
    }

    public AsyncLazy(Func<Task<T>> factory)
    {
        instance = new Lazy<Task<T>>(() => Task.Run(factory));
    }

    public TaskAwaiter<T> GetAwaiter()
    {
        return instance.Value.GetAwaiter();
    }
}

Die AsyncLazy Klasse kombiniert die Lazy<T> und Task<T> typen, um eine lazy-initialisierte Aufgabe zu erstellen, die die Initialisierung einer Ressource darstellt. Der Fabrikstellvertretung, der an den Konstruktor übergeben wird, kann entweder synchron oder asynchron sein. Factory-Stellvertretungen werden auf einem Threadpoolthread ausgeführt und werden nicht mehr als einmal ausgeführt (auch wenn mehrere Threads versuchen, sie gleichzeitig zu starten). Wenn ein Fabrikstellvertretung abgeschlossen ist, ist der lazy-initialisierte Wert verfügbar, und alle Methoden, die auf die AsyncLazy<T> Instanz warten, erhalten den Wert. Weitere Informationen finden Sie unter AsyncLazy.

Methoden zur Datenbearbeitung

Die TodoItemDatabase Klasse enthält Methoden für die vier Arten der Datenbearbeitung: Erstellen, Lesen, Bearbeiten und Löschen. Die SQLite.NET-Bibliothek bietet eine einfache relationale Objektzuordnung (ORM), mit der Sie Objekte speichern und abrufen können, ohne SQL Anweisungen zu schreiben.

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

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

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

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

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

Zugreifen auf Daten in Xamarin.Forms

Die TodoItemDatabase Klasse stellt das Feld zur Verfügung, durch das die Instance Datenzugriffsvorgänge in der TodoItemDatabase Klasse aufgerufen werden können:

async void OnSaveClicked(object sender, EventArgs e)
{
    var todoItem = (TodoItem)BindingContext;
    TodoItemDatabase database = await TodoItemDatabase.Instance;
    await database.SaveItemAsync(todoItem);

    // Navigate backwards
    await Navigation.PopAsync();
}

Erweiterte Konfiguration

SQLite bietet eine robuste API mit mehr Features als in diesem Artikel und der Beispiel-App. Die folgenden Abschnitte umfassen Features, die für die Skalierbarkeit wichtig sind.

Weitere Informationen finden Sie in der SQLite-Dokumentation zu sqlite.org.

Schreibgeschützte Protokollierung

Standardmäßig verwendet SQLite ein herkömmliches Rollback-Journal. Eine Kopie des unveränderten Datenbankinhalts wird in eine separate Rollbackdatei geschrieben, dann werden die Änderungen direkt in die Datenbankdatei geschrieben. Das COMMIT tritt auf, wenn das Rollback-Journal gelöscht wird.

Write-Ahead Protokollierung (WAL) schreibt Änderungen zuerst in eine separate WAL-Datei. Im WAL-Modus ist ein COMMIT ein spezieller Datensatz, der an die WAL-Datei angefügt ist, wodurch mehrere Transaktionen in einer einzelnen WAL-Datei auftreten können. Eine WAL-Datei wird wieder in die Datenbankdatei in einem speziellen Vorgang namens Prüfpunkt zusammengeführt.

WAL kann für lokale Datenbanken schneller sein, da Leser und Autoren sich nicht blockieren, sodass Lese- und Schreibvorgänge gleichzeitig ausgeführt werden können. Der WAL-Modus erlaubt jedoch keine Änderungen an der Seitengröße, fügt der Datenbank zusätzliche Dateizuordnungen hinzu und fügt den zusätzlichen Prüfpunktvorgang hinzu.

Um WAL in SQLite.NET zu aktivieren, rufen Sie die EnableWriteAheadLoggingAsync Methode auf der SQLiteAsyncConnection Instanz auf:

await Database.EnableWriteAheadLoggingAsync();

Weitere Informationen finden Sie unter SQLite Write-Ahead Protokollierung auf sqlite.org.

Kopieren einer Datenbank

Es gibt mehrere Fälle, in denen es erforderlich sein kann, eine SQLite-Datenbank zu kopieren:

  • Eine Datenbank wurde mit Ihrer Anwendung geliefert, muss jedoch kopiert oder in schreibbarem Speicher auf dem mobilen Gerät verschoben werden.
  • Sie müssen eine Sicherung oder Kopie der Datenbank vornehmen.
  • Sie müssen die Datenbankdatei versionieren, verschieben oder umbenennen.

Im Allgemeinen ist das Verschieben, Umbenennen oder Kopieren einer Datenbankdatei der gleiche Prozess wie jeder andere Dateityp mit einigen zusätzlichen Überlegungen:

  • Alle Datenbankverbindungen sollten geschlossen werden, bevor Sie versuchen, die Datenbankdatei zu verschieben.
  • Wenn Sie die Write-Ahead-Protokollierung verwenden, erstellt SQLite eine Datei für den freigegebenen Speicherzugriff (.shm) und eine Datei (Write Ahead Log) (Wal). Stellen Sie sicher, dass Sie auch Änderungen an diesen Dateien anwenden.

Weitere Informationen finden Sie unter "Dateibehandlung" in Xamarin.Forms.