Xamarin база данных, работа с библиотекой SQLite

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

Интегрируйте 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, чтобы отложить инициализацию базы данных до первого обращения к ней:

public class TodoItemDatabase
{
    static SQLiteAsyncConnection Database;


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


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


    //...
}

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

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

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

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

public class AsyncLazy
{
    readonly Lazy<>> instance;


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


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


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

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

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

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

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


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


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


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


    public Task 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 использует традиционный журнал отката. Копия неизмененного содержимого базы данных записывается в отдельный файл отката, после чего изменения записываются непосредственно в файл базы данных. Фиксация возникает при удалении журнала отката.

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

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

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

await Database.EnableWriteAheadLoggingAsync();

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

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

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

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

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

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

Материалы по теме