Статьи

Новый SQLite Wrapper для Windows Phone 8 и Windows 8 — Основы

Одной из тем, которая привлекла больше внимания в моем блоге, является SQLite: разработка приложения без необходимости каким-либо образом хранить данные локально практически невозможна, поэтому разработчики всегда ищут лучший способ достижения этой цели. SQLite является одним из наиболее интересных решений, поскольку он имеет открытый исходный код и доступен практически для любой платформы на рынке (мобильной, веб, клиентской и т. Д.).

Я уже говорил вам о sqlite-net , библиотеке, доступной для Windows Phone 8 и Windows 8, которую можно использовать для выполнения операций над базой данных SQLite с помощью API высокого уровня, поддерживающего операции LINQ. Эта библиотека очень проста в использовании, но у нее есть некоторые ограничения: самая большая из них — это то, что она не поддерживает отношения из коробки.

Питер Торр (член команды разработчиков Windows Phone в Microsoft) при поддержке Энди Уигли (бывшего MVP-разработчика Windows Phone Development, который теперь присоединился к Microsoft) выпустил на Codeplex новую оболочку SQLite, которая полностью отличается от sqlite- сеть, и это удовлетворяет другому типу подхода: тотальный контроль. Фактически, эта новая оболочка не поддерживает никакие операции LINQ, а только ручную инструкцию SQL: самое большое преимущество в том, что вы можете выполнять любые операции, даже управлять отношениями. Самым большим минусом является то, что вам придется полагаться на старые простые SQL-запросы для выполнения любых операций, даже самых простых, таких как создание таблицы или вставка некоторых данных.

Давайте посмотрим, шаг за шагом, как его использовать и чем отличается подход от использования sqlite-net. В этом первом посте мы рассмотрим основы, в следующем мы поговорим об отношениях.

Настройте оболочку

На данный момент, поскольку она написана на собственном коде, библиотека недоступна в NuGet: вам придется загрузить исходный код со страницы Codeplex . Решение содержит два проекта: один называется SQLiteWinRT, а другой — SQLiteWinRTPhone. Первый предназначен для приложений Магазина Windows, второй — для приложений Windows Phone 8 (Windows Phone 7 не поддерживается, поскольку он не поддерживает собственный код): вам придется добавить в свое решение проект, который соответствует вашим потребностям , Для этого урока я собираюсь создать приложение для Windows Phone 8, поэтому я буду использовать второй проект. Кроме того, как и в случае с sqlite-net, нам нужно установить официальную среду выполнения SQLite, которая доступна в виде расширения Visual Studio: вот ссылкадля версии приложений Магазина Windows здесь указана ссылка на версию Windows Phone 8.

Теперь вы готовы использовать его! Мы собираемся использовать тот же пример, который мы использовали в посте sqlite-net: таблицу для хранения данных о людях.

Создать базу данных

Давайте посмотрим, как это сделать:

private async void OnCreateDatabaseClicked(object sender, RoutedEventArgs e)
{
    Database database = new Database(ApplicationData.Current.LocalFolder, "people.db");
 
    await database.OpenAsync();
 
    string query = "CREATE TABLE PEOPLE " +
                   "(Id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," +
                   "Name varchar(100), " +
                   "Surname varchar(100))";
 
    await database.ExecuteStatementAsync(query);
}

Сначала мы создаем новый объект базы данных , который мы собираемся использовать для выполнения операций над базой данных. Существуют различные способы его создания: в этом примере мы собираемся создать его, указав папку в изолированном хранилище, в котором следует сохранить файл, и имя файла. Папка передается в конструктор с помощью объекта StorageFolder , являющегося классом среды выполнения Windows, который идентифицирует папку в изолированном хранилище. В примере мы просто используем корень хранилища. Если база данных не существует, она будет создана; в противном случае он будет просто открыт.

Затем мы можем открыть соединение, вызвав метод OpenAsync () : он принимает, в конце концов, параметр для установки режима открытия (например, только для чтения). Если мы не установим его, он будет использоваться по умолчанию, который поддерживает операции чтения и записи.

Тогда мы идем вручную! Как уже ожидалось, оболочка не поддерживает операции LINQ, поэтому вам придется написать необходимые операторы SQL для выполнения операции. Я предполагаю, что вы уже знакомы с основами SQL, поэтому я не буду подробно описывать запросы: в этом примере мы определяем запрос для создания таблицы с именем People с 3 столбцами: Id (который является первичным ключом и является автоматическим значение приращения), Имя и Фамилия (которая просто содержит строки).

Для выполнения запроса мы вызываем асинхронный метод ExecuteStatementAsync () для объекта базы данных , передавая в качестве параметра строку с оператором запроса. ExecuteStatementAsync () — это метод, который нам нужно использовать, когда запрос не возвращает никакого значения и когда нам не нужно определять какой-либо параметр (позже мы увидим, как их использовать).

Выполнять операции с базой данных

Подход к выполнению операций (вставка, обновление, выбор и т. Д.) Тот же, что мы видели: мы открываем соединение, определяем оператор запроса и выполняем запрос. Единственное отличие состоит в том, что, например, когда вы делаете вставку, вам обычно нужно установить некоторые параметры, потому что некоторые элементы запроса не фиксированные, а динамические. Например, если мы хотим добавить новую строку в таблицу « Люди», которую мы создали на предыдущем шаге, нам нужно передать два динамических значения: Имя и Фамилия .

Вот код для выполнения этой операции:

private async void OnAddDataClicked(object sender, RoutedEventArgs e)
{
    Database database = new Database(ApplicationData.Current.LocalFolder, "people.db");
 
    await database.OpenAsync();
 
    string query = "INSERT INTO PEOPLE (Name, Surname) VALUES (@name, @surname)";
    Statement statement = await database.PrepareStatementAsync(query);
    statement.BindTextParameterWithName("@name", "Matteo");
    statement.BindTextParameterWithName("@surname", "Pagani");
 
    await statement.StepAsync();
 
    statement = await database.PrepareStatementAsync(query);
    statement.BindTextParameterWithName("@name", "John");
    statement.BindTextParameterWithName("@surname", "Doe");
 
    await statement.StepAsync();
}

Пожалуйста, введите класс Statement , который можно использовать для выполнения дополнительных операций над запросом, таких как добавление параметров и перебор результатов. Чтобы подготовить объект Statement , вам нужно вызвать метод PrepareStatementAsync (), передав в качестве параметра запрос для выполнения. Как управлять параметрами? Самый простой способ — использовать именованные параметры, которые можно добавить в запрос, просто добавив символ @ к имени параметра. В этом примере запрос вставки принимает два параметра: @name и @surname .

Как определить значение этих параметров? Называя BindTextParameterWithName () метод на Statement объекта с двумя параметрами: первый из них является имя параметра, то второй из них является значение , которое мы хотим назначить. В этом примере мы добавим в таблицу двух пользователей: одного с именем и одного с именем Джон Доу . Чтобы выполнить запрос, мы вызываем метод StepAsync () для объекта Statement . Существуют также другие версии метода BindParameterWithName () в зависимости от типа данных параметра (например, если это число, вы можете использовать метод BindIntParameterWithName () ).

Но у метода StepAsync () есть и другая цель: его также можно использовать для итерации результатов запроса, если мы выполняем запрос, который может вернуть одно или несколько значений. Давайте посмотрим, например, как выполнить выборку для извлечения данных, которые мы только что вставили:

private async void OnGetDataClicked(object sender, RoutedEventArgs e)
{
    Database database = new Database(ApplicationData.Current.LocalFolder, "people.db");
 
    await database.OpenAsync();
 
    string query = "SELECT * FROM PEOPLE";
    Statement statement = await database.PrepareStatementAsync(query);
 
    while (await statement.StepAsync())
    {
        MessageBox.Show(statement.GetTextAt(0) + " " + statement.GetTextAt(1));
    }
}

Первая часть кода та же, что мы видели раньше: мы определяем запрос (в этом случае мы получаем все строки таблицы People ), мы готовим Statement и выполняем его с помощью метода StepAsync () . Разница заключается в том, что на этот раз, StepAsync () метод выполняется в то время как утверждение: это потому , что этот метод будет перебирать все строки , возвращаемые запросом так, каждый раз , когда мы входим в то время как цикл, новая строка извлекается , В этом примере мы ожидаем увидеть MessageBox дважды: один для пользователя Matteo Pagani и один для пользователя John Doe . В примере вы также видите, как получить значения из результатов:Объект Statement предлагает несколько методов, которые начинаются с префикса Get , который принимает в качестве параметра индекс столбца значения, которое нужно получить. Существует несколько методов для наиболее распространенных типов данных: в этом примере, поскольку и Имя, и Фамилия являются строками, мы используем метод GetTextAt () , передавая 0 в качестве индекса для получения имени и 1 в качестве индекса для получения фамилии.

Конечно, мы можем объединить то, что мы узнали в последних двух примерах, и, например, мы можем выполнить запрос на выборку, который содержит некоторые параметры:

private async void OnGetSomeDataClicked(object sender, RoutedEventArgs e)
{
    Database database = new Database(ApplicationData.Current.LocalFolder, "people.db");
 
    await database.OpenAsync();
 
    string query = "SELECT * FROM PEOPLE WHERE Name=@name";
    Statement statement = await database.PrepareStatementAsync(query);
    statement.BindTextParameterWithName("@name", "Matteo");
 
    while (await statement.StepAsync())
    {
        MessageBox.Show(statement.GetTextAt(0) + " " + statement.GetTextAt(1));
    }
}

В этом примере мы извлекаем из таблицы People только те строки, в которых столбец Name содержит значение Matteo.

У нас также есть другая опция для доступа к столбцам строк, которые мы извлекли с помощью запроса, но по умолчанию она отключена, поскольку она медленнее и возвращает каждое значение в виде строки, а не ее правильный тип. Чтобы включить его, вы должны вызвать метод EnableColumnsProperty объекта Statement : как только вы это сделаете, вы можете получить доступ к значениям, используя свойство Columns объекта Statement : это коллекция, и вы можете получить доступ к каждому элементу, используя имя столбца в качестве индекса. Вот образец

private async void OnGetSomeDataWithColumnsPropertyClicked(object sender, RoutedEventArgs e)
{
    Database database = new Database(ApplicationData.Current.LocalFolder, "people.db");
 
    await database.OpenAsync();
 
    string query = "SELECT * FROM PEOPLE";
    Statement statement = await database.PrepareStatementAsync(query);
 
    statement.EnableColumnsProperty();
 
    while (await statement.StepAsync())
    {
        MessageBox.Show(statement.Columns["Name"] + " " + statement.Columns["Surname"]);
    }
}

Скоро

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

Скачать проект