Поиск на сайте: Расширенный поиск


Новые программы oszone.net Читать ленту новостей RSS
CheckBootSpeed - это диагностический пакет на основе скриптов PowerShell, создающий отчет о скорости загрузки Windows 7 ...
Вы когда-нибудь хотели создать установочный диск Windows, который бы автоматически установил систему, не задавая вопросо...
Если после установки Windows XP у вас перестала загружаться Windows Vista или Windows 7, вам необходимо восстановить заг...
Программа подготовки документов и ведения учетных и отчетных данных по командировкам. Используются формы, утвержденные п...
Red Button – это мощная утилита для оптимизации и очистки всех актуальных клиентских версий операционной системы Windows...
OSzone.net Microsoft Разработка приложений Windows Phone Sterling для изолированного хранилища в Windows Phone 7 RSS

Sterling для изолированного хранилища в Windows Phone 7

Текущий рейтинг: 4 (проголосовало 2)
 Посетителей: 1240 | Просмотров: 1710 (сегодня 0)  Шрифт: - +

Официальный выпуск Windows Phone 7 дал возможность примерно миллиону разработчиков для Silverlight практически за ночь стать кодировщиками для мобильных устройств.

Приложения для Windows Phone 7 пишутся на том же языке (C# или Visual Basic) и в инфраструктуре, почти идентичной версии Silverlight 3 под браузеры, которая позволяет, в том числе, структурировать экраны с помощью XAML и редактировать их в Expression Blend. Однако в разработке для Windows Phone 7 возникают свои уникальные трудности, включая необходимость в особом управлении при переключении пользователя между приложениями в сочетании с ограниченной поддержкой управления состоянием.

Sterling — это проект базы данных с открытым исходным кодом, основанный на изолированном хранилище, которое помогает управлять локальными объектами и ресурсами в вашем приложении Windows Phone 7, а также упрощает процесс переключения между приложениями. Эта объектно-ориентированная база данных проектировалась в расчете на компактность, она быстро работает и позволяет решать такие задачи, как сохранение, кеширование и управление состоянием. Она работает с существующими определениями ваших типов, не требуя никаких изменений в них.

В этой статье я покажу разработчикам для Windows Phone 7, как использовать библиотеку Sterling для сохранения и запроса локальных данных с минимальными усилиями, а также продемонстрирую простую стратегию управления состоянием, когда приложение деактивируется в процессе замораживания (tombstoning).

Основы процесса замораживания

Silverlight в браузере и на смартфоне выполняется в специальной изолированной среде («песочнице»), которая полностью отделяет хост-систему от исполняющей среды приложения. В среде на смартфоне добавляется еще один уровень сложности, так как на этой платформе может сосуществовать несколько приложений. Хотя нижележащая ОС смартфона поддерживает многозадачность, сторонним приложениям не разрешается доступ к этому уровню. Вместо этого приложения получают возможность выполнения «на переднем плане» (foreground), но могут быстро вытесняться, уступая дорогу другим приложениям, входящим телефонным звонкам или аппаратным средствам вроде кнопки Back и поиска. Когда приложение деактивируется, существует вероятность того, что оно будет «убито» с возможностью оживления, если пользователь вернется к нему. Этот процесс называется замораживанием (tombstoning).

Обновление Mango для Windows Phone 7 ограничит возможный круг сценариев с замораживанием «быстрым переключением приложений». Приложения больше не будут автоматически замораживаться. Хотя эта функциональность уже присутствует в Windows Phone 7 Developer Tools, важно понимать, что это не исключает сценариев с замораживанием. На вероятность замораживания приложения будут влиять такие факторы, как другие выполняемые приложения и объем доступной памяти.

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

Сохранение состояния на смартфоне

К счастью, в Windows Phone 7 есть несколько механизмов сохранения состояния. Для работы с приложениями в этой ОС вы должны ознакомиться с этими способами. К ним относятся использование SQL CE (с обновлением Mango), словарь состояния страницы, словарь состояния приложения и изолированное хранилище. Я уделю основное внимание последнему варианту, изолированному хранилищу, но вкратце обрисую и первые три, чтобы вы понимали, почему Sterling — столь полезная технология на смартфонах под управлением Windows Phone 7.

SQL CE В обновлении Mango вы получите SQL CE — компактную версию популярной базы данных SQL Server. Разница между этой базой данных и другими вариантами заключается в том, что SQL является реляционной базой данных. Она опирается на особые табличные форматы, которые создаются на основе ваших классов и элементов управления. Объектно-ориентированные базы данных могут принимать существующие структуры классов (даже если они включают вложенные классы, списки и другие типы) и сериализовать их без дополнительного преобразования или модификации.

Состояние страницы У каждого объектаPage есть свойство State, которое предоставляет словарь, определяемый по именам ключей и связанным объектам. Использовать можно любой ключ или объект, но объект должен быть сериализуемым; кроме того, учтите, что осуществляется лишь высокоуровневая сериализация. Состояние страницы может хранить до 2 Мб данных на страницу (не более 4 Мб на все приложение) и применимо лишь после вызова метода OnNavigatedTo и до вызова метода OnNavigatedFrom для данной страницы. Это ограничивает на практике использование простых значимых типов. Тот факт, что состояние работает лишь внутри методов навигации по страницам, означает, что это плохой выбор для шаблонов вроде Model-View-ViewModel (MVVM), синхронизирующих состояние представления с применением отдельного ViewModel.

Состояние приложения Это тоже словарь. Подобно состоянию страницы он принимает строковый ключ и значение объекта; объект должен быть сериализуемым. Объект приложения в Phone имеет событие Deactivated, вызываемое при замораживании, и Activated, генерируемое при возврате приложения из замороженного состояния (при размораживании). Состояние приложения доступно в любой момент между активацией и деактивацией. Доступ к нему осуществляется через статический класс PhoneApplicationService (свойство Current ссылается на единственный экземпляр, глобальный в рамках приложения). Лимиты на размер словаря состояния приложения не документированы, но при попытке сохранить слишком много элементов генерируется необрабатываемое COM-исключение.

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


Изолированное хранилище На данный момент это наиболее гибкий вариант поддержания состояния. Изолированное хранилище не уникально для Phone и фактически работает почти так же в Silverlight и в исполняющей среде Microsoft .NET Framework. Оно создает уровень абстракции от файловой системы хоста, поэтому вместо прямого обращения к хранилищу файлов вы взаимодействуете с механизмом, который предоставляет папки и файлы в изолированной «песочнице». В Windows Phone 7 эта «песочница» изолируется на уровне вашего Phone-приложения. Вы можете обращаться к этому хранилищу из любой части своего приложения, но не из другого приложения, выполняемого в Phone.

Изолированное хранилище дает несколько крупных преимуществ. Оно не только предоставляет словарь, аналогичный ранее описанным словарям страницы и приложения, но и позволяет организовывать данные в папки и файлы. По сути, в изолированном хранилище можно создавать любой тип файлов — XML, двоичный или текстовый — и работать с ними. Размер изолированного хранилища в Phone не ограничивается, поэтому реальный лимит зависит от объемов памяти, доступных в Phone. Единственный недостаток в том, что процессы записи и чтения из этого хранилища несколько медленнее, чем при других способах, когда списки хранятся в активной памяти.

Варианты сериализации

Другое преимущество изолированного хранилища — возможность выбора нескольких стратегий сериализации. В отличие от параметров, позволяющих назначать ключ и объект, механизм изолированного хранилища предоставляет файловый поток, в который можно записывать текст, XML, JSON и даже двоичный код. Это упрощает сериализацию объектов многих типов, в том числе «глубокую» сериализацию, дающей возможность обрабатывать сложные графы объектов и сериализовать любые объекты-потомки в экземпляре, с которым вы работаете.

JSON иXML Это две основные стратегии сериализации, доступные в Silverlight. XML применим, если вы используете либо DataContractSerializer (который генерирует XML), либо XMLSerializer. Первый знаком разработчикам, использующим Windows Communication Foundation (WCF), и именно эта стратегия применяется инфраструктурой для гидратации и дегидратации сообщений, посылаемых через веб-сервисы. Для этого данные должны быть помечены атрибутами DataContract и DataMember. Это в основном подход по соглашению, так как вы явно помечаете поля, которые хотите сериализовать. XmlSerializer сериализует все открытые свойства с аксессорами get и set, и вы можете изменять поведение с помощью особых атрибутов, специфичных для XML. Подробнее о DataContractSerializer см. по ссылке bit.ly/fUDPha, а подробнее о XmlSerializer — по ссылке bit.ly/fCIa6q.

JSON расшифровывается как JavaScript Object Notation и популярен в Web, поскольку этот формат легко преобразуется в JavaScript-объекты. Некоторые разработчики предпочитают этот вариант, так как читаемый текст размещается в сериализованном объекте в более компактной форме, чем в случае XML. JSON-сериализация реализуется с применением специальной версии сериализатора контрактов данных — DataContractJsonSerializer. Генерируемый им вывод занимает значительно меньше места на диске, чем XML. Подробнее о DataContractJsonSerializer см. по ссылке bit.ly/8cFyjV.

Двоичный формат Silverlight и Windows Phone 7 также могут использовать двоичную сериализацию. В Silverlight нет BinaryFormatter (это класс, помогающий автоматически сериализовать объекты в двоичную форму), поэтому вы должны обрабатывать такую сериализацию самостоятельно. Чтобы использовать двоичную сериализацию, вы просто создаете компонент двоичной записи (binary writer) и записываете в поток. Этот компонент обрабатывает многие примитивы, поэтому по большей части можно записывать свойства вашего класса для сериализации экземпляра, а впоследствии создавать экземпляр и заполнять его свойства с помощью компонента чтения (reader). Однако в проектах с множеством классов это может привести к нагромождению. Вот здесь на сцену выходит Sterling.

Sterling Разработан специально для упрощения сериализации и десериализации объектов в Silverlight. Изначально созданный для Silverlight 4 в браузере, Sterling постепенно эволюционировал в почти идентичную кодовую базу, которая поддерживает Windows Phone 7. За кулисами Sterling использует двоичную сериализацию, результаты которой очень компактны и занимают на диске минимум места. Sterling позволяет сериализовать почти все классы и организует экземпляры по предоставляемым вами ключам (любое свойство в экземпляре может быть обозначено как ключ). Sterling также предоставляет индексы, которые можно запрашивать в памяти для большей скорости перед загрузкой всего экземпляра с диска. Sterling обеспечивает полный контроль над нижележащим потоком сериализации; вы можете шифровать, сжимать или даже переопределять двоичный поток, чтобы сериализовать типы именно так, как вам нужно.

Другое преимущество изолированного хранилища — возможность выбора нескольких стратегий сериализации.


Одно из преимуществ Sterling — быстрый и простой способ сериализации объектов с возможностью моментально запрашивать ключи и индексы, используя LINQ to Objects. Он также обрабатывает внешние ключи и отношения. За все эти преимущества при сериализации и десериализации вы расплачиваетесь лишь небольшим снижем быстродействия.

На рис. 1 сравниваются различные стратегии сериализации и относительные размеры результатов на диске.

Рис. 1. Сравнение различных стратегий сериализации в отношении занимаемого дискового пространства

*
Увеличить

Для генерации этих показателей было создано 2000 контактов с использованием случайных имен и адресов. В каждой записи о контакте хранятся полное имя, адрес и уникальный идентификатор (рис. 2).

Рис. 2. Класс Contact

*

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

Полная сериализация на диск и десериализация с него при использовании Sterling была лишь немного медленнее из-за издержек прохода по объектному графу и обработки индексов и ключей. На диаграмме на рис. 3 сравнивается скорость работы при использовании каждого варианта для сохранения и загрузки всех 2000 контактов.

Рис. 3. Сравнение скорости

*
Увеличить

В запросе Sterling сканировал весь набор в поисках контактов с именами, которые начинаются на букву «L», а затем загружал все эти контакты. В примере запрос выдал 65 контактов из 2000. Sterling отфильтровал и загрузил их всего за 110 мс по сравнению с двумя секундами, которые потребовались при использовании других вариантов.

Приложение-пример для работы с рецептами

Чтобы продемонстрировать применение Sterling в Windows Phone 7, я написал небольшую программу для работы с рецептами, которая позволяет просматривать, редактировать и добавлять новые рецепты. Каждый рецепт состоит из названия, набора инструкций, категории (например, ланч или обед) и набора ингредиентов. У ингредиентов могут быть такие параметры, как количество, мера и пищевой продукт. Продукты можно добавлять «на лету». Пример страницы из этого приложения показан на рис. 4.

*

Рис. 4. Экран редактирования рецепта

В этом приложении Sterling служит трем целям. Во-первых, он помогает заранее загрузить справочные данные, используемые приложением, например категории, единицы измерений, названия исходных продуктов и примеры рецептов. Во-вторых, он сохраняет данные, которые вводят пользователи при добавлении собственных рецептов или названий пищевых продуктов. Наконец, он облегчает замораживание с помощью сериализации состояния приложения при деактивации. Приложение-пример использует шаблон MVVM и демонстрирует, как осуществлять замораживание из ViewModel.

Первым шагом, разумеется, будет добавление Sterling в проект. Это можно сделать через NuGet (просто введите «Sterling» для поиска) или скачиванием двоичных файлов и всех файлов исходного кода с сайта CodePlex по ссылке sterling.codeplex.com.

Подготовка базы данных

Следующий шаг —подготовка базы данных. Ее конфигурация определяет, какие типы будут сохраняться и какие ключи и индексы будут использоваться. База данных Sterling — это класс, созданный наследованием от BaseDatabaseInstance. Вы должны предоставить две перегрузки: уникальное имя для базы данных (имя должно быть уникально в каждом приложении; кроме того, можно размещать несколько баз данных для разделения данных или управления разными версиями) и список определений таблиц. В базовом классе содержатся вспомогательные методы для определения таблиц, ключей и индексов.

Ключи и индексы В этом приложении определена таблица типа FoodModel с использованием объекта Id в качестве ключа с индексом по свойству FoodName. Это приводит к созданию в памяти списка объектов, который можно быстро запрашивать и фильтровать.

В следующем коде определяется «таблицы» food с целочисленным ключом и строковым индексом:

CreateTableDefinition<FoodModel, int>(f => f.Id)
.WithIndex<FoodModel, string, int>(IDX_FOOD_NAME,f=>f.FoodName)

В вызове для создания определения таблицы передаются тип класса, тип ключа и лямбда-выражение для разрешения ключа. В качестве ключа в своем классе вы можете использовать любое уникальное значение, отличное от null. Метод расширения индекса принимает типы таблицы, индекса и ключа. В данном случае имя индекса определяется константой, а лямбда-выражение предоставляет значение, которое будет использоваться индексом.

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

В приложении-примере (оно находится в пакете исходного кода, который можно скачать для этой статьи) все сущности используют целочисленный ключ. Поэтому для генерации ключа можно создать обобщенный триггер, основанный на типе экземпляра. При первом создании экземпляра триггер запрашивает от базы данных существующие ключи и находит ключ с самым большим значением. Если записи отсутствуют, начальное значение ключа будет 1. При каждом сохранении экземпляра с ключом, значение которого меньше или равно 0, будет назначаться следующий номер. Код этого базового триггера показан на рис. 5.

Рис. 5. Базовый триггер для автоматической генерации ключей

public class IdentityTrigger<T> : BaseSterlingTrigger<T,int>
  where T: class, IBaseModel, new()
{
  private static int _idx = 1;

  public IdentityTrigger(ISterlingDatabaseInstance database)
  {
    // Если запись существует, присваиваем ей
    // наивысшее значение плюс 1
    if (database.Query<T,int>().Any())
    {
      _idx = database.Query<T, int>().Max(key => key.Key) + 1;
    }
  }

  public override bool BeforeSave(T instance)
  {
    if (instance.Id < 1)
    {
      instance.Id = _idx++;
    }

    return true;
  }

  public override void AfterSave(T instance)
  {
    return;
  }

  public override bool BeforeDelete(int key)
  {
    return true;
  }
}

Заметьте, что в запросе допустимы стандартные LINQ-выражения вроде Any и Max. Кроме того, разработчик отвечает за обеспечение безопасности механизма триггера в многопоточной среде.

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

Собственный сериализатор Sterling изначально поддерживает довольно много типов. Встречая классы и структуры, он перебирает открытые свойства и поля этих классов для сериализации контента. Подклассы и структуры также перебираются рекурсивно. Sterling не может сериализовать некоторые базовые типы напрямую. Например, System.Type определен как абстрактный класс, и от него может наследовать много классов. Sterling не в состоянии напрямую сериализовать или десериализовать этот тип. Для поддержки замораживания будет создан особый класс, который хранит свойства ViewModel и использует тип ViewModel в качестве ключа. Чтобы обрабатывать этот тип, в Sterling можно создать собственный сериализатор.

Для создания собственного сериализатора наследуйте от класса BaseSerializer и создайте перегрузки (перегруженные версии методов). В собственном классе TypeSerializer поддерживается любой класс, производный от System.Type, и при сериализации просто записывается полное имя типа с указанием сборки. Для получения типа из сборки по полному имени при десериализации используется статический метод GetType класса Type. Результат показан на рис. 6. Заметьте: он явным образом поддерживает любой тип, производный от System.Type (или присваиваемый ему).

Рис. 6. TypeSerializer

public class TypeSerializer : BaseSerializer
{
  /// <summary>
  ///   Возвращает true, если этот сериализатор может
  ///   обработать объект, т. е., если его можно
  ///   привести к типу
  /// </summary>
  /// <param name="targetType">The target</param>
  /// <returns>True if it can be serialized</returns>
  public override bool CanSerialize(Type targetType)
  {
    return typeof (Type).IsAssignableFrom(targetType);
  }

  /// <summary>
  ///   Сериализует объект
  /// </summary>
  /// <param name="target">The target</param>
  /// <param name="writer">The writer</param>
  public override void Serialize(object target,
    BinaryWriter writer)
  {
    var type = target as Type;
    if (type == null)
    {
      throw new SterlingSerializerException(
        this, target.GetType());
    }
    writer.Write(type.AssemblyQualifiedName);
  }

  /// <summary>
  ///   Десериализует объект
  /// </summary>
  /// <param name="type">The type of the object</param>
  /// <param name="reader">A reader to deserialize from</param>
  /// <returns>The deserialized object</returns>
  public override object Deserialize(
    Type type, BinaryReader reader)
  {
    return Type.GetType(reader.ReadString());
  }
}

Любые собственные сериализаторы регистрируются в ядре Sterling, прежде чем их можно будет активировать.

Создание и сохранение данных

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

Для поддержки процесса размораживания ядро базы данных Sterling активируется, когда активируется само приложение, и деактивируется, когда оно замораживается или закрывается. Это гарантирует сброс ключей и индексов базы данных на диск и стабильность состояния базы данных. В файле App.xaml.cs эти события можно подключить к жизненному циклу Phone-приложения. Для подготовки базы данных достаточно нескольких строк кода:

_engine = new SterlingEngine();
_engine.SterlingDatabase.RegisterSerializer<TypeSerializer>();
_engine.Activate();
Database =
  _engine.SterlingDatabase.RegisterDatabase<RecipeDatabase>();

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

Database.Flush();
_engine.Dispose();
Database = null;
_engine = null;

Как только база данных активирована, она готова к приему данных. Наиболее распространенный способ упаковки данных — их включение в виде встроенного ресурса в читаемом формате, например XML, JSON или CSV. Запрос к базе данных позволяет определить, существуют ли данные и, если их нет, загрузить. Сохранение данных в Sterling осуществляется довольно прямолинейно: вы просто передаете экземпляр для сохранения, а об остальном заботится Sterling. На рис. 7 представлен запрос, который проверяет категории. Если категорий нет, данные считываются из файлов встроенных ресурсов для начального заполнения базы данных. Обратите внимание на то, что перед заполнением базы данных сначала выполняется операция Truncate для очистки таблиц.

Рис. 7. Заполнение начальными данными

if (database.Query<CategoryModel, int>().Any()) return;

// Избавляемся от любых старых данных
database.Truncate(typeof(MeasureModel));
database.Truncate(typeof(FoodModel));

var idx = 0;

foreach(var measure in ParseFromResource(FILE_MEASURES,
  line =>
  new MeasureModel
  { Id = ++idx, Abbreviation = line[0], FullMeasure = line[1]}
  ))
{
  database.Save(measure);
}

// Автоматическая генерация идентификаторов
// по названиям пищевых продуктов
foreach (var food in
  ParseFromResource(FILE_FOOD, line
    => new FoodModel { FoodName = line[0] })
    .Where(food => !string.IsNullOrEmpty(food.FoodName)))
{
  database.Save(food);
}

var idx1 = 0;

foreach (var category in ParseFromResource(FILE_CATEGORIES,
  line =>
  new CategoryModel { Id = ++idx1, CategoryName = line[0] }))
{
  database.Save(category);
}

Основной ViewModel: категории и рецепты

После того как база данных заполнена начальной информацией и загружена, остальная часть приложения может выводить на основе существующих данных списки и приглашения пользователю, а также сохранять любые сведения, вводимые им. Ниже дан пример того, как начальные данные становятся доступными приложению. В следующем фрагменте кода все категории загружаются в наблюдаемый (observable) набор в основном ViewModel:

Categories = new ObservableCollection<CategoryModel>();
foreach(var category in App.Database.Query<
  CategoryModel,int>())
{
  Categories.Add(category.LazyValue.Value);
}

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

Следующий фрагмент XAML показывает, как этот элемент управления напрямую связывается с набором и выбранной категорией:

<controls:Pivot
  x:Name="pivotMain"
  Title="Sterling Recipes"
  ItemsSource="{Binding Categories}"
  SelectedItem="{Binding CurrentCategory,Mode=TwoWay}">

Редактирование ингредиентов: внешние ключи

Главное в рецептах — это, конечно, ингредиенты. Приложение содержит «главный список» названий пищевых продуктов и мер. Таким образом, в рецепте может быть список ингредиентов, включающий количество, тип единицы измерения и название пищевого продукта. Нажатием кнопки Ingredients (рис. 4) пользователь открывает список ингредиентов, где может добавлять, удалять или редактировать существующие ингредиенты.

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

*

Рис. 8. Иерархия классов рецептов

Рецепты содержат списки ингредиентов, в которых есть круговая ссылка обратно на родительский рецепт. Ингредиенты также содержат модель Amount, в которую входят единицы и меры.

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

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

Поиск ингредиентов: запросы с индексами

Sterling используется ключи и индексы для ускорения работы запросов и фильтров. Пример фильтрации запроса — поиск ингредиентов. Текстовое поле связано с ViewModel и будет обновлять набираемый текст по мере его ввода. При наборе текста пользователь будет мгновенно видеть результаты (названия пищевых продуктов, в которых обнаруживается совпадение). Это сужает область поиска для пользователя, и он может либо выбрать предлагаемый вариант, либо ввести следующую букву. Поиск ингредиентов демонстрируется на рис. 9, где показываются названия, в которых есть буквы «pe».

*

Рис. 9. Поиск ингредиентов, названия которых включают буквы «pe»

Изначально Sterling был разработан специально для упрощения сериализации и десериализации объектов в Silverlight.


Всякий раз, когда пользователь вводит очередную букву, аксессор set свойства в ViewModel обновляется искомым текстом. В свою очередь этот аксессор генерирует событие изменения свойства для списка пищевых продуктов. Список выдает новый запрос к базе данных и получает результаты через LINQ to Objects. На рис. 10 показан запрос, который использует индекс для фильтрации и упорядочения данных. Чтобы получить доступ к этому запросу, вызывается база данных с передачей типов класса, индекса и ключа, а также посылается имя индекса. Заметьте, что новая модель Food создается на основе ключа и значения индекса.

Рис. 10. Запрос Food

public IEnumerable<FoodModel> Food
{
  get
  {
    if (string.IsNullOrEmpty(_foodText))
    {
      return Enumerable.Empty<FoodModel>();
    }
    var foodTextLower = _foodText.ToLower();
    return from f in App.Database.Query<FoodModel,
      string, int>(RecipeDatabase.IDX_FOOD_NAME)
      where f.Index.ToLower().Contains(foodTextLower)
      orderby f.Index
      select new FoodModel { Id = f.Key, FoodName = f.Index };
  }
}

Замораживание на практике

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

Шаблон MVVM снимает ответственность за поддержку замораживания с XAML и отделенного кода, ориентированных на представление, так как состояние представления синхронизируется с ViewModel через механизм связывания с данными. Благодаря этому каждый ViewModel может сохранять и восстанавливать свое состояние. Механизм связывания с данными будет соответственно обновлять представление. Для упрощения поддержки замораживания создан класс TombstoneModel.

Ему присваивается ключ на основе типа (т. е. интерфейса для сохраняемого ViewModel), и он содержит словарь ключей и объектов. Это обеспечивает максимальную гибкость в хранении типов или классов — так, как это нужно для сохранения состояния ViewModel.

Sterling поддерживает это потому, что при сериализации объект записывается на диск как реализованный тип (implemented type) независимо от того, как определено свойство — как обобщенный объект, интерфейс или абстрактный базовый класс. Это невозможно сделать с помощью встроенных сериализаторов. Чтобы предоставить общий интерфейс, в облегченной версии инфраструктуры MVVM есть интерфейс ITombstoneFriendly. В этом интерфейсе определены методы активации и деактивации, вызываемые при навигации пользователя в связанное представление или переходе из него (например, замораживание инициирует событие перехода из представления).

Тогда замораживание сводится к созданию модели, заданию типа и присваивания ему значений, которые нужно сохранить. Размораживание включает загрузку модели, чтение значений  восстановление состояния ViewModel. На рис. 11 эти этапы иллюстрируются в ViewModel текстового редактора, который должен сохранять переданный ему заголовок и текст, набранный пользователем.

Рис. 11. Замораживание

/// <summary>
///   Замораживание
/// </summary>
public void Deactivate()
{
  var tombstone = new TombstoneModel
    {SyncType = typeof (ITextEditorViewModel)};
  tombstone.State.Add(ExtractPropertyName(()=>Title), Title);
  tombstone.State.Add(ExtractPropertyName(() =>Text), Text);
  App.Database.Save(tombstone);
}

/// <summary>
///   Размораживание
/// </summary>
public void Activate()
{
  var tombstone = App.Database.Load<TombstoneModel>
    (typeof(ITextEditorViewModel));
  if (tombstone == null) return;
  Title = tombstone.TryGet(ExtractPropertyName(() =>
    Title), string.Empty);
  Text = tombstone.TryGet(ExtractPropertyName(() =>
    Text), string.Empty);
}

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

Компактность и гибкость Sterling

Как видите, Sterling снимает с разработчиков бремя реализации сложных стратегий сериализации. Для сохранения сущностей достаточно определить тип класса и лямбда-выражение, возвращающее уникальный ключ. Компактность Sterling (на момент написания этой статьи размер DLL составлял менее 100 Кб) и его гибкость (подключение триггеров, шифрование, сжатие и поддержка собственной сериализации) дает возможность эффективно выполнять большинство задач, связанных с локальными встраиваемыми базами данных, кешированием и замораживанием. На примере приложения для работы с рецептами вы увидели, как Sterling интегрируется с Windows Phone 7 и насколько легко он позволяет решать эти задачи.

Автор: Джереми Ликнесс  •  Иcточник: MSDN Magazine  •  Опубликована: 05.12.2011
Нашли ошибку в тексте? Сообщите о ней автору: выделите мышкой и нажмите CTRL + ENTER
Теги:   Windows Phone 7, Sterling.


Оценить статью:
Вверх
Комментарии посетителей
Комментарии отключены. С вопросами по статьям обращайтесь в форум.