Xamarin.Forms Lokalne bazy danych
Aparat bazy danych SQLite umożliwia Xamarin.Forms aplikacjom ładowanie i zapisywanie obiektów danych w kodzie udostępnionym. Przykładowa aplikacja używa tabeli bazy danych SQLite do przechowywania zadań do wykonania. W tym artykule opisano sposób używania SQLite.Net w kodzie udostępnionym do przechowywania i pobierania informacji w lokalnej bazie danych.
Zintegruj SQLite.NET z aplikacjami mobilnymi, wykonując następujące kroki:
- Zainstaluj pakiet NuGet.
- Konfigurowanie stałych.
- Utwórz klasę dostępu do bazy danych.
- Uzyskiwanie dostępu do danych w programie Xamarin.Forms.
- Konfiguracja zaawansowana.
Instalowanie pakietu NuGet SQLite
Użyj menedżera pakietów NuGet, aby wyszukać plik sqlite-net-pcl i dodać najnowszą wersję do projektu kodu udostępnionego.
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 nawet w projektach .NET Standard.
Ważne
SQLite.NET to biblioteka innej firmy obsługiwana przez repozytorium praeclarum/sqlite-net.
Konfigurowanie stałych 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
{
get
{
var basePath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
return Path.Combine(basePath, DatabaseFilename);
}
}
}
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 SQLiteOpenFlags
programu , 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. Aplikacja Todo definiuje klasę TodoItemDatabase
w tym celu.
Inicjowanie z opóźnieniem
Używa TodoItemDatabase
asynchronicznej inicjowania z opóźnieniem reprezentowanego przez klasę niestandardową AsyncLazy<T>
, aby opóźnić inicjowanie bazy danych do momentu uzyskania do niego dostępu:
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);
}
//...
}
Pole Instance
jest używane do tworzenia tabeli bazy danych dla TodoItem
obiektu, jeśli jeszcze nie istnieje, i zwraca wartość jako pojedynczą TodoItemDatabase
. Pole Instance
typu AsyncLazy<TodoItemDatabase>
jest konstruowane po raz pierwszy, gdy jest oczekiwane. Jeśli wiele wątków próbuje jednocześnie uzyskać dostęp do pola, wszystkie będą używać pojedynczej konstrukcji. Następnie po zakończeniu budowy wszystkie await
operacje zakończą się. Ponadto wszystkie await
operacje po zakończeniu budowy są kontynuowane natychmiast, ponieważ wartość jest dostępna.
Uwaga
Połączenie z bazą danych jest polem statycznym, które zapewnia, że pojedyncze połączenie z bazą danych jest używane przez cały czas działania aplikacji. Użycie trwałego połączenia statycznego zapewnia lepszą wydajność niż wielokrotne otwieranie i zamykanie połączeń w ramach jednej sesji aplikacji.
Inicjowanie asynchroniczne z opóźnieniem
Aby rozpocząć inicjowanie bazy danych, unikać blokowania wykonywania i mieć możliwość przechwycenia wyjątków, przykładowa aplikacja używa asynchronicznej opóźnianej intalizacji reprezentowanej przez klasę AsyncLazy<T>
:
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();
}
}
Klasa AsyncLazy
łączy Lazy<T>
typy i Task<T>
w celu utworzenia z opóźnieniem zainicjowanego zadania, które reprezentuje inicjowanie zasobu. Delegat fabryki przekazany do konstruktora może być synchroniczny lub asynchroniczny. Delegaty fabryki będą uruchamiane w wątku puli wątków i nie będą wykonywane więcej niż raz (nawet jeśli wiele wątków próbuje je uruchomić jednocześnie). Po zakończeniu delegata fabryki jest dostępna z opóźnieniem zainicjowana wartość, a wszystkie metody oczekujące AsyncLazy<T>
na wystąpienie otrzymają wartość. Aby uzyskać więcej informacji, zobacz AsyncLazy.
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.
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);
}
}
Uzyskiwanie dostępu do danych w Xamarin.Forms
Klasa TodoItemDatabase
uwidacznia Instance
pole, za pomocą którego można wywołać operacje dostępu do danych w TodoItemDatabase
klasie:
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();
}
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.
Aby uzyskać więcej informacji, zobacz Obsługa plików w programie Xamarin.Forms.