Локальные базы данных .NET MAUI

Browse sample. Обзор примера

Ядро СУБД SQLite позволяет приложениям .NET Multi-platform App UI (.NET MAUI) загружать и сохранять объекты данных в общем коде. Вы можете интегрировать SQLite.NET в приложения .NET MAUI для хранения и получения информации в локальной базе данных, выполнив следующие действия.

  1. Установите пакет NuGet.
  2. Настройте константы.
  3. Создайте класс доступа к базе данных.
  4. Доступ к данным.
  5. Расширенная настройка.

В этой статье используется пакет NuGet sqlite-net-pcl для предоставления доступа к базе данных SQLite к таблице для хранения элементов todo. Альтернативой является использование пакета NuGet Microsoft.Data.Sqlite , который является упрощенным поставщиком ADO.NET для SQLite. Microsoft.Data.Sqlite реализует общие ADO.NET абстракции для таких функций, как подключения, команды и средства чтения данных.

Установка пакета NuGet SQLite

Используйте диспетчер пакетов NuGet для поиска пакета sqlite-net-pcl и добавьте последнюю версию в проект приложения .NET MAUI.

Существует ряд пакетов NuGet с похожими названиями. Правильный пакет имеет следующие атрибуты:

  • Идентификатор: sqlite-net-pcl
  • Авторы: SQLite-net
  • Владелец: praeclarum
  • Ссылка в NuGet:sqlite-net-pcl

Несмотря на имя пакета, используйте пакет NuGet sqlite-net-pcl в проектах .NET MAUI.

Важно!

SQLite.NET — это сторонняя библиотека, поддерживаемая репозиторием praeclarum/sqlite-net.

Установка SQLitePCLRaw.bundle_green

Помимо sqlite-net-pcl, необходимо временно установить базовую зависимость, которая предоставляет SQLite на каждой платформе:

  • Идентификатор: SQLitePCLRaw.bundle_green
  • Версия:>= 2.1.0
  • Авторы: Эрик Приемник
  • Владельцы: Эрик Приемник
  • Ссылка NuGet:SQLitePCLRaw.bundle_green

Настройка констант приложений

Данные конфигурации, такие как имя файла базы данных и путь, могут храниться в виде констант в приложении. Пример проекта включает файл Constants.cs , предоставляющий общие данные конфигурации:

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

В этом примере файл констант указывает значения перечисления по умолчанию SQLiteOpenFlag , используемые для инициализации подключения к базе данных. Перечисление SQLiteOpenFlag поддерживает следующие значения:

  • Create: подключение автоматически создаст файл базы данных, если он не существует.
  • FullMutex: подключение открывается в сериализованном режиме потоков.
  • NoMutex: подключение открывается в режиме многопотокового режима.
  • PrivateCache: подключение не будет участвовать в общем кэше, даже если оно включено.
  • ReadWrite: подключение может считывать и записывать данные.
  • SharedCache: подключение будет участвовать в общем кэше, если оно включено.
  • ProtectionComplete: файл зашифрован и недоступен, пока устройство заблокировано.
  • ProtectionCompleteUnlessOpen: файл шифруется до его открытия, но затем доступен, даже если пользователь блокирует устройство.
  • ProtectionCompleteUntilFirstUserAuthentication: файл шифруется до тех пор, пока пользователь не загрузился и разблокировал устройство.
  • ProtectionNone: файл базы данных не зашифрован.

Возможно, вам потребуется указать разные флаги в зависимости от того, как будет использоваться база данных. Дополнительные сведения см. в SQLiteOpenFlagsстатье "Открытие новой базы данных Подключение на sqlite.org".

Создание класса доступа к базе данных

Класс оболочки базы данных абстрагирует уровень доступа к данным от остальной части приложения. Этот класс централизованно выполняет логику запросов и упрощает управление инициализацией базы данных, что упрощает рефакторинг или расширение операций с данными по мере роста приложения. Пример приложения определяет TodoItemDatabase класс для этой цели.

Отложенная инициализация

Использует TodoItemDatabase асинхронную отложенную инициализацию для задержки инициализации базы данных до тех пор, пока он не будет первым доступом, с простым Init методом, вызываемым каждым методом в классе:

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

Методы обработки данных

Класс TodoItemDatabase включает методы для четырех типов манипуляций с данными: создание, чтение, редактирование и удаление. Библиотека SQLite.NET предоставляет простую карту реляционных объектов (ORM), которая позволяет хранить и извлекать объекты без написания инструкций SQL.

В следующем примере показаны методы обработки данных в примере приложения:

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

Доступ к данным

Класс TodoItemDatabase можно зарегистрировать как одинтон, который можно использовать во всем приложении, если вы используете внедрение зависимостей. Например, вы можете зарегистрировать страницы и класс доступа к базе данных в качестве служб в объекте IServiceCollection в MauiProgram.cs с AddSingleton помощью методов и AddTransient методов:

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

builder.Services.AddSingleton<TodoItemDatabase>();

Затем эти службы можно автоматически внедрить в конструкторы классов и получить к ней доступ:

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

Кроме того, можно создать новые экземпляры класса доступа к базе данных:

TodoItemDatabase database;

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

Дополнительные сведения о внедрении зависимостей в приложениях .NET MAUI см. в статье об внедрении зависимостей.

Расширенная конфигурация

SQLite предоставляет надежный API с большими функциями, чем описано в этой статье, и пример приложения. В следующих разделах рассматриваются функции, важные для масштабируемости.

Дополнительные сведения см . в документации ПО SQLite по sqlite.org.

Ведение журнала накануне записи

По умолчанию SQLite использует традиционный журнал отката. Копия без изменений содержимого базы данных записывается в отдельный файл отката, а изменения записываются непосредственно в файл базы данных. Фиксация возникает при удалении журнала отката.

Ведение журнала на основе записи (WAL) сначала записывает изменения в отдельный WAL-файл. В режиме WAL фиксация — это специальная запись, добавленная в WAL-файл, которая позволяет выполнять несколько транзакций в одном wal-файле. Wal-файл объединяется обратно в файл базы данных в специальной операции с именем проверка point.

Wal может быть быстрее для локальных баз данных, так как читатели и записи не блокируют друг друга, что позволяет выполнять операции чтения и записи одновременно. Однако режим WAL не позволяет изменять размер страницы, добавляет дополнительные связи файлов в базу данных и добавляет дополнительную операцию проверка назначения.

Чтобы включить WAL в SQLite.NET, вызовите EnableWriteAheadLoggingAsync метод в экземпляре SQLiteAsyncConnection :

await Database.EnableWriteAheadLoggingAsync();

Дополнительные сведения см. в статье SQLite Write-Ahead Logging on sqlite.org.

Копирование базы данных

Существует несколько случаев, когда может потребоваться скопировать базу данных SQLite:

  • База данных поставляется с приложением, но должна быть скопирована или перемещена в записываемое хранилище на мобильном устройстве.
  • Необходимо создать резервную копию или копию базы данных.
  • Необходимо изменить версию, переместить или переименовать файл базы данных.

Как правило, перемещение, переименование или копирование файла базы данных является тем же процессом, что и любой другой тип файла с несколькими дополнительными рекомендациями.

  • Все подключения к базе данных следует закрыть перед попыткой переместить файл базы данных.
  • Если вы используете ведение журнала на основе записи, SQLite создаст файл общего доступа к памяти (SHM) и файл (запись вперед) (wal). Убедитесь, что к этим файлам также применяются любые изменения.