Lokales Speichern von Daten mit SQLite

Abgeschlossen

SQLite ist nützlich, wenn Sie mit relationalen Daten arbeiten. Angenommen, Sie erstellen eine Social Media-App. Sie müssen Informationen zu Abonnenten der App speichern. Diese Daten enthalten eine eindeutige ID für jeden Benutzer und dessen Namen. Sie können diese Art von Beziehung in einer SQLite-Datenbank ganz einfach modellieren.

In dieser Lerneinheit erfahren Sie, wie Sie SQLite mithilfe von SQLite-net in einer .NET MAUI-Anwendung verwenden können.

Was ist SQLite?

SQLite ist eine einfache plattformübergreifende lokale Datenbank, die inzwischen Branchenstandard für mobile Anwendungen ist. Für SQLite ist kein Server erforderlich. Die Datenbank wird in einer einzelnen Datenträgerdatei im Dateisystem des Geräts gespeichert. Alle Lese- und Schreibvorgänge werden direkt in dieser SQLite-Datenträgerdatei ausgeführt.

Die nativen SQLite-Bibliotheken sind standardmäßig in Android und iOS integriert, der Engine unterstützt aber nur eine C/C++-API. Dieses Szenario ist keine ideale Lösung für .NET-Entwickler*innen, die eine Möglichkeit zur Interaktion von SQLite und .NET benötigen.

Was ist SQLite-net?

Es gibt verschiedene C#-Wrapper um die native SQLite-Engine, die .NET-Entwickler nutzen können. Viele .NET-Entwickler verwenden einen beliebten C#-Wrapper namens SQLite-net.

SQLite-net ist eine objektrelationale Zuordnung. Dadurch kann der Definitionsprozess für Datenbankschemata vereinfacht werden, weil Sie die Modelle, die in Ihren Projekten definiert sind, als Schema verwenden können.

Diagram showing how SQLite-net provides a .NET wrapper and the SQLite C/C++ engine.

Sehen Sie sich als Beispiel die folgende Klasse an, die einen User modelliert:

class User
{
    public int Id { get; set; }
    public string Username { get; set; }
    ...
}

Mithilfe einer objektrelationalen Zuordnung können Sie diese initiale User-Klasse verwenden und eine Datenbanktabelle namens User erstellen, die Spalten für die Felder Id und Username in dieser Klasse enthält.

SQLite-net wird als NuGet-Paket bereitgestellt. Sie müssen das Paket sqlite-net-pcl zu Ihren Apps hinzufügen, um es verwenden zu können. Verwenden Sie den NuGet-Paket-Manager in Visual Studio. Wenn Sie eine App unter Android ausführen möchten, müssen Sie außerdem das Paket SQLitePCLRaw.provider.dynamic_cdecl hinzufügen.

Herstellen einer Verbindung mit einer SQLite-Datenbank

Sie können eine Verbindung mit einer SQLite-Datenbank aus einer App über ein SQLiteConnection-Objekt herstellen. Diese Klasse wird im Namespace SQLite zusammen mit den anderen von SQLite bereitgestellten Typen und Methoden definiert. Wenn Sie dieses Objekt instanziieren, übergeben Sie den Dateinamen für die Datenbankdatei. Der Konstruktor öffnet dann entweder die Datei, wenn sie vorhanden ist, oder er erstellt sie, wenn sie nicht vorhanden ist.

Der folgende Code zeigt ein Beispiel:

using SQLite;
...
string filename = ...
SQLiteConnection conn = new SQLiteConnection(filename);

Denken Sie daran, dass filename auf einen Speicherort in der App-Sandbox verweisen sollte.

Erstellen einer Tabelle

Da SQLite-net eine objektrelationale Zuordnung ist, können Sie Ihr Datenbankschema aus C#-Klassen aufbauen. SQLite-net kann eine Datenbanktabelle aus einer normalen C#-Klasse erstellen. Es gibt aber viele Attribute, die Sie einer Klasse hinzufügen können, um zusätzliche Metadaten bereitzustellen. Mithilfe dieser Metadaten kann SQLite Features wie z. B. Eindeutigkeit erzwingen und Einschränkungen auf Ihre Daten anwenden.

Zu den verfügbaren Attributen gehören folgende:

  • Table: Geben Sie den Namen der Tabelle an, wenn dieser sich vom Namen der Klasse unterscheiden soll
  • PrimaryKey: Geben Sie eine Spalte als Primärschlüssel an
  • AutoIncrement: Geben Sie an, dass eine Spalte automatisch den Wert erhöht, wenn eine neue Zeile eingefügt wird
  • Column: Geben Sie den Namen einer Spalte an, wenn dieser sich vom Namen der Eigenschaft unterscheiden soll
  • MaxLength: Geben Sie die maximale Anzahl von Zeichen an, die in der Spalte enthalten sein dürfen
  • Unique: Geben Sie an, dass der Wert in der Spalte eindeutig sein und sich von allen anderen Zeilen unterscheiden muss

Der folgende Code zeigt die aktualisierte Version der User-Klasse, die diese Attribute anwendet:

[Table("user")]
public class User
{
    // PrimaryKey is typically numeric 
    [PrimaryKey, AutoIncrement, Column("_id")]
    public int Id { get; set; }

    [MaxLength(250), Unique]
    public string Username { get; set; }
    ...
}

Rufen Sie nach dem Definieren der C#-Klasse die generische Methode CreateTable für die SQLiteConnection-Klasse auf, um die Tabelle in der Datenbank zu generieren. Geben Sie die Klasse als Typparameter an. Hier sehen Sie ein Beispiel:

SQLiteConnection conn = new SQLiteConnection(filename);
conn.CreateTable<User>();

Wenn die Tabelle in der Datenbank bereits vorhanden ist, überprüft die CreateTable-Methode das Schema, um zu ermitteln, ob es Änderungen gibt. Falls das zutrifft, versucht der Vorgang, das Datenbankschema zu aktualisieren.

Ausführen von grundlegenden Lese- und Schreibvorgängen

Nachdem Sie eine Tabelle erstellt haben, können Sie mit ihr interagieren. Verwenden Sie zum Hinzufügen einer Zeile die Insert-Methode für die SQLiteConnection-Instanz, und stellen Sie ein Objekt des entsprechenden Typs bereit, das die einzufügenden Daten enthält. Der folgende Code zeigt, wie Sie der Tabelle User eine neue Zeile hinzufügen:

public int AddNewUser(User user)
{
    int result = conn.Insert(user);
    return result;
}

Die Insert-Methode gibt einen Wert für int zurück, der für die Anzahl der Zeilen steht, die in die Tabelle eingefügt wurden. In diesem Fall wurde eine Zeile eingefügt.

Verwenden Sie zum Abrufen von Zeilen aus einer Tabelle die Table-Methode. Diese Methode gibt eine Auflistung von Objekten zurück (die leer sein kann):

public List<User> GetAllUsers()
{
    List<User> users = conn.Table<User>().ToList();
    return users;
}

Die Methode Table gibt ein TableQuery\<T>-Objekt zurück. Um ein List abzurufen, verwenden Sie die ToList-Methode, wie im vorherigen Beispiel gezeigt.

Ausführen einer SQLite-Abfrage mit LINQ

Die Table-Methode ruft alle Zeilen aus einer Tabelle ab. In den meisten Fällen möchten Sie nur eine Teilmenge der Zeilen zurückgeben, die einer Menge von angegebenen Kriterien entsprechen. Verwenden Sie für diese Aufgaben LINQ mit SQLite-net.

SQLite-net unterstützt viele häufige LINQ-Abfragen, darunter:

  • Hierbei gilt:
  • Take
  • Überspringen
  • OrderBy
  • OrderByDescending
  • ThenBy
  • ElementAt
  • First (Erster)
  • FirstOrDefault
  • ThenByDescending
  • Anzahl

Mit diesen Methoden können Sie die Syntax von Erweiterungsmethoden oder die LINQ-C#-Syntax verwenden. Hier sehen Sie beispielsweise einen Codeausschnitt, mit dem Sie die Details zu einem angegebenen Benutzer abrufen können:

public User GetByUsername(string username)
{
    var user = from u in conn.Table<User>()
               where u.Username == username
               select u;
    return user.FirstOrDefault();
}

Aktualisieren und Löschen von Zeilen

Sie aktualisieren eine Zeile mithilfe der Update-Methode des SQLiteConnection-Objekts. Sie stellen ein Objekt bereit, das die Zeile definiert, die mit dessen neuen Werten aktualisiert werden soll. Die Update-Methode ändert die Zeile, die denselben Primärschlüsselwert wie das bereitgestellte Objekt hat. Bei dem zurückgegebenen Wert handelt es sich um die Anzahl der geänderten Zeilen. Wenn dieser Wert 0 ist, wurden keine Zeilen mit einem übereinstimmenden Primärschlüssel gefunden, und es wurde nichts aktualisiert. Der nächste Codeausschnitt zeigt diese Methode in Aktion:

public int UpdateUser(User user)
{
    int result = 0;
    result = conn.Update(user);
    return result;
}

Entfernen sie Zeilen aus einer Tabelle mit der Delete-Methode des SQLiteConnection-Objekts. Die einfachste Form dieser Methode verwendet den Primärschlüssel des zu löschenden Elements als Parameter, wie unten gezeigt wird. Diese Form der Delete-Methode ist generisch und erfordert einen Typparameter. Der zurückgegebene Wert ist die Anzahl der Zeilen, die aus der Tabelle entfernt wurden:

public int DeleteUser(int userID)
{
    int result = 0;
    result = conn.Delete<User>(userID);
    return result;
}