Xamarin.Forms Локальные базы данных

Download Sample Скачайте пример

Ядро СУБД SQLite позволяет Xamarin.Forms приложениям загружать и сохранять объекты данных в общем коде. В примере приложения для хранения элементов todo используется таблица базы данных SQLite. В этой статье описывается, как использовать SQLite.Net в общем коде для хранения и получения информации в локальной базе данных.

Screenshots of the Todolist app on iOS and Android

Интеграция SQLite.NET в мобильные приложения, выполнив следующие действия.

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

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

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

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

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

Пакет NuGet sqlite-net-pcl следует использовать даже в проектах .NET Standard.

Внимание

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

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

Пример проекта содержит файл 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
    {
        get
        {
            var basePath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
            return Path.Combine(basePath, DatabaseFilename);
        }
    }
}

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

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

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

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

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

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

Использует TodoItemDatabase асинхронную отложенную инициализацию, представленную пользовательским AsyncLazy<T> классом, чтобы отложить инициализацию базы данных до тех пор, пока она не будет впервые доступ к ней:

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

    //...
}

Поле Instance используется для создания таблицы базы данных для TodoItem объекта, если она еще не существует, и возвращает ее TodoItemDatabase в виде одного. Instance Поле типа AsyncLazy<TodoItemDatabase> создается при первом ожидании. Если несколько потоков пытаются получить доступ к полю одновременно, они будут использовать одну конструкцию. Затем, когда строительство завершится, все await операции завершались. Кроме того, все await операции после завершения строительства продолжаются немедленно, так как значение доступно.

Примечание.

Подключение к базе данных — это статическое поле, которое гарантирует, что для жизни приложения используется одно подключение к базе данных. Использование постоянного статического подключения обеспечивает лучшую производительность, чем открытие и закрытие подключений несколько раз во время одного сеанса приложения.

Асинхронная отложенная инициализация

Чтобы запустить инициализацию базы данных, избежать блокировки выполнения и получить возможность перехвата исключений, пример приложения использует асинхронную отложенную инициализацию, представленную классом 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();
    }
}

Класс AsyncLazy объединяет Lazy<T> и типы для создания отложенной инициализации Task<T> задачи, представляющей инициализацию ресурса. Делегат фабрики, переданный конструктору, может быть синхронным или асинхронным. Делегаты фабрики будут выполняться в потоке пула потоков и не будут выполняться несколько раз (даже если несколько потоков пытаются запустить их одновременно). После завершения делегата фабрики доступно отложенное инициализированное значение, и все методы, ожидающие AsyncLazy<T> получения экземпляром значения. Дополнительные сведения см. в разделе AsyncLazy.

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

Класс TodoItemDatabase включает методы для четырех типов манипуляций с данными: создание, чтение, редактирование и удаление. Библиотека SQLite.NET предоставляет простую карту реляционных объектов (ORM), которая позволяет хранить и извлекать объекты без написания инструкций 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);
    }
}

Доступ к данным в Xamarin.Forms

Класс TodoItemDatabase предоставляет Instance поле, с помощью которого можно вызвать операции доступа к данным в TodoItemDatabase классе:

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

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

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). Убедитесь, что к этим файлам также применяются любые изменения.

Дополнительные сведения см. в разделе "Обработка файлов".Xamarin.Forms