Разработка преобразователя данных

Неотъемлемой частью разработки объектной модели карточки является разработка преобразователя данных — объект, в котором объясняется каким образом элементы объектной модели карточки (или секций) связаны с данными этой карточки в базе данных.

Контекст объектов задействует преобразователь данных на этапе создания экземпляра карточки (к примеру, методом GetObject), а также при сохранении новых или изменённых данных.

Преобразователь данных разрабатывается для каждого созданного класса строки секции, а также для самой карточки.

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

Далее рассматривается пример разработки преобразователей для строк секций и карточки, рассмотренных ранее.

Преобразователь данных для строки секции

Базовым классом преобразователя для строки (не зависит от типа секции) является класс CardRowMapper. Для класса CardRowMapper указывается тип, соответствующий классу объектной модели строки для которой он создаётся.

Для строки плоской секции, разработанной ранее, преобразователь будет выглядеть следующим образом:
internal sealed class SampleCardStructSectionMapper : CardRowMapper<SampleCardStructSection>
{
 private static ObjectMap map;

 static SampleCardStructSectionMapper() (1)
 {
  SampleCardStructSectionMapper.InitializeObjectMap(); (2)
 }

 public SampleCardStructSectionMapper(ObjectContext context) : base(context) (3)
 {
 }

 protected override ObjectMap GetObjectMap() (4)
 {
  return SampleCardStructSectionMapper.map;
 }

 protected override SampleCardStructSection CreateObject(ObjectInitializationData data) (5)
 {
  return new SampleCardStructSection(data);
 }

 private static void InitializeObjectMap() (6)
 {
  SampleCardStructSectionMapper.map = new ObjectMap();

  SampleCardStructSectionMapper.map.ObjectTypeId = new Guid("78006F34-55DF-497F-BD62-E0A33C8EEABF"); (7)

  SampleCardStructSectionMapper.map.Field(SampleCardStructSection.NameProperty, "Name"); (8)

  SampleCardStructSectionMapper.map.Reference(SampleCardStructSection.AuthorProperty, "Author"); (9)
 }
}
1 Обязательный статический конструктор.
2 Создаем карту преобразователя.
3 Обязательный конструктор для передачи текущего контекста объектов.
4 Обязательный метод, предоставляющей карту.
5 Создание экземпляра объекта секции с заранее подготовленными данными.
6 Создание карты преобразователя.
7 Указываем идентификатор секции в схеме карточки.
8 Связываем обычное поле.

Первый аргумент объявление поля в классе секции, а второй аргумент — название поля в схеме карточки.

9 Связываем ссылочное поле.

Преобразовать для строки коллекционной секции не отличается от преобразователя для строки плоской секции.

Отличие в реализации преобразователя для строки иерархической секции в наличии в карте объявления подчиненных секций:
public class SampleCardTreeSectionMapper : CardRowMapper<SampleCardTreeSection>
{
 private static ObjectMap map;
 static SampleCardTreeSectionMapper()
 {
  SampleCardTreeSectionMapper.InitializeObjectMap();
 }
 public SampleCardTreeSectionMapper(ObjectContext context) : base(context)
 {
 }
 protected override ObjectMap GetObjectMap()
 {
  return SampleCardTreeSectionMapper.map;
 }
 protected override SampleCardTreeSection CreateObject(ObjectInitializationData data)
 {
  return new SampleCardTreeSection(data);
 }

 private static void InitializeObjectMap()
 {
  SampleCardTreeSectionMapper.map = new ObjectMap();
  SampleCardTreeSectionMapper.map.ObjectTypeId = new Guid("08ACDC17-EAB7-4E66-8C6D-428F09490F3A");

  SampleCardTreeSectionMapper.map.Collection(SampleCardTreeSection.DescendantsProperty, new Guid("80DB0D9B-D1E8-4A4D-8001-E71AC07B10CE")); (1)
 }
}
1 В карту добавляется подчиненная секция. Первым аргументом передается объявление секции в родительской секции, а вторым — её идентификатор из схемы метаданных.

Преобразователь данных для карточки

Преобразователь данных для карточки оформляется аналогичным образом, за исключением базового класса (должен быть класс BaseCardMapper<T>) и того, что в карте присутствуют исключительно секции.

public class SampleCardMapper : CardMapper<SampleCard>
{
 private static ObjectMap map;
 static SampleCardMapper()
 {
  SampleCardMapper.InitializeObjectMap();
 }
 public SampleCardMapper(ObjectContext context) : base(context)
 {
 }
 protected override ObjectMap GetObjectMap()
 {
  return SampleCardMapper.map;
 }
 protected override SampleCard CreateObject(ObjectInitializationData data)
 {
  return new SampleCard(data);
 }
 private static void InitializeObjectMap()
 {
  SampleCardMapper.map = new ObjectMap();

  SampleCardMapper.map.ObjectTypeId = new Guid("E1BF5846-FE0C-424B-9B71-B58D1A526BCF"); (1)

  SampleCardMapper.map.Collection(SampleCard.StructSectionProperty, new Guid("A3F0B456-587E-4769-B019-467AD4EB9BF8")); (2)
  SampleCardMapper.map.Collection(SampleCard.CollectionSectionProperty, new Guid("F6E9E009-7447-435D-8DB8-C3E4187E2D61"));
  SampleCardMapper.map.Collection(SampleCard.TreeSectionProperty, new Guid("EA968311-3702-4568-9663-2CE9CBE09CC6"));
 }
}
1 Идентификатор типа карточки.
2 Регистрация секций в карте преобразователя.

Преобразователь данных для справочника

Класс преобразователя данных для справочника реализуется по аналогии с преобразователя данных карточки. Единственное отличие — базовый класс здесь DictionaryMapper<T>.

public class SampleDictionaryMapper : DictionaryMapper<SampleDictionary>
{
 private static ObjectMap map;
 static SampleDictionaryMapper()
 {
  SampleDictionaryMapper.InitializeObjectMap();
 }
 public SampleDictionaryMapper(ObjectContext context) : base(context)
 {
 }
 protected override ObjectMap GetObjectMap()
 {
  return SampleDictionaryMapper.map;
 }
 protected override SampleDictionary CreateObject(ObjectInitializationData data)
 {
  return new SampleDictionary(data);
 }
 private static void InitializeObjectMap()
 {
  SampleDictionaryMapper.map = new ObjectMap();

  SampleDictionaryMapper.map.ObjectTypeId = new Guid("4E32BC51-5F50-4F68-9D0F-3BFBB6523B14"); (1)
  SampleDictionaryMapper.map.Collection(SampleDictionary.TreeSectionProperty, new Guid("0CD96A19-59EF-44EC-AF47-977A56BDDB32"));
 }
}
1 Идентификатор типа справочника.

Регистрация преобразователей данных

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

public sealed class SampleCardsMapperFactory : ObjectMapperFactory
{
 public SampleCardsMapperFactory(ObjectContext context) : base(context)
 {
  base.RegisterObjectMapper(typeof(SampleCardStructSection), typeof(SampleCardStructSectionMapper)); (1)
  base.RegisterObjectMapper(typeof(SampleCardCollectionSection), typeof(SampleCardCollectionSectionMapper));
  base.RegisterObjectMapper(typeof(SampleCardTreeSection), typeof(SampleCardTreeSectionMapper));
  base.RegisterObjectMapper(typeof(SampleCard), typeof(SampleCardMapper));
 }
}
1 В метод передается тип объектной модели строки секции или карточки, а также тип соответствующего преобразователя.

Чтобы контекст объектов мог работать с типами карточек и секций, для которых разработаны фабрики преобразователей данных, эти фабрики должны быть добавлены в контекст (см. на примере добавления фабрики BackOfficeMapperFactory в примере).