Работа с библиотекой SQLite для Xamarin

Ядро СУБД SQLite позволяет Xamarin.Forms приложениям загружать и сохранять объекты данных в общем коде. В примере приложения для хранения элементов списка дел используется таблица базы данных SQLite. В этой статье описывается, как использовать 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.

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

Пример проекта включает файл 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: файл базы данных не зашифрован.

Может потребоваться указать разные флаги в зависимости от способа использования базы данных.

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

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

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

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

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

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

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

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

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

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

await Database.EnableWriteAheadLoggingAsync();

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

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

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

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

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

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

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