Идеологически мой проект является не просто приложением, но расширяемой и настраиваемой платформой. Это громкое звание достигается за счет нескольких функциональных возможностей, заложенных в систему, одной из них является возможность кастомизации (customizing - настройка, расширение функциональности) сущностей приложения. Иными словами говоря, администратор системы в приложении-конфигураторе выбирает сущности, которыми оперирует система и добавляет или удаляет поля, управляет их отображением в UI. Эту функциональность реализует customfields "framework" (опять я использовал это громкое слово).
- добавление и удаление полей сущностей в run-time;
- кастомизация представлений в различных приложениях и wizard'ах системы (расположение и отображение заложенных в сущность и только что созданных полей);
- хеш-таблица в базе данных, которая хранит пару имя поля и значение, проиндексированные по уникальному ключу сущности;
- таблица с настройками, в которой в виде CLOB хранится xaml представлений и список custom полей;
- административный сервис, который поставляет/сохраняет список custom полей
- сервис, который поставляет собственно список custom полей по уникальному идентификатору сущности, поставляет/сохраняет UI представления (lose-xaml) в БД.
- DataModel и CustomFieldsProvider - шлюзы данных для UI.
Как вы поняли, реализация предельно простая. Custom представления хранятся в виде xaml (форматированный xml). Для отображения этого xaml во всех представлениях используется написанный мною примитивный контрол XamlViewer, о которм я уже упоминал при описании локализации форматированного текста.
Итак, самое интересное о чем я вам хочу рассказать - реализация шлюза данных для сущности с custom полями.
Решений данной задачи несколько и, поскольку Blend 1 не поддерживает формат решений VS2008, то для совместной работы с дизайнером, мы остаемся в VS2005 и пишем, используя .Net 3.0, соответственно и решения будут C# 2 специфичные. Это я к тому, что в C# 3 появилась замечательная функциональность (я бы даже сказал синтаксический сахар) - методы расширения. С ними решение было бы просто тривиальным. Но поскольку мы ограничены исполняемой средой, то и будем исходить из этого. Итак, решения:
- Для каждой сущности написать отдельный класс DataModel, поставляющий только кастомные поля;
- Или же сделать получение кастомных полей прозрачным для клиента, использующего DataModel - поставщик сущности.
Второй вариант звучит неплохо, но как его реализовать? Обратимся к классикам - в книге GoF описан структурный паттерн Decorator, описание которого дословно звучит: "Динамически возлагает на объект новые функции. Декораторы применяются для расширения имеющейся функциональности и являются гибкой альтернативой порождению подклассов". Бинго! То что нужно. Ведь данное решение позволит добавить к имеющемуся объекту custom поля и мы сможем динамически, основываясь на различных данных (к примеру права доступа), подменять объект его декоратором.
Приступим. Прежде всего опрделимся кого же мы будем декорировать. Если вы помните в посте про DM-V-VM я описал generic-класс SEModel (Single Entity Model). Вот его мы и будем декорировать.
Прежде всего определим интерфейс для CustomFieldsModelDecorator (CFModelDecorator).
На диаграмме вы видите ICFModel - интерфейс для модели, поставляющей custom fields. Custom поля хранятся в специальной коллекции ObservableDictionary - словаре, реализующем databinding-специфичные интерфейсы INotifyPropertyChanged, INotifyCollectionChanged. Готовой реализации в BCL нет, поэтому я написал свою примитивную обертку над Dictionary.
Теперь реализация декоратора:
CFDataModel - generic класс как и SEModel и он также реализует интерфейс ISEModel. Он параметризуется интерфейсом оборачиваемой модели, теми же типами сущности и интерфейсом провайдера, что и оборачиваемая модель, + интерфейсом провайдера custom полей.
В конструктор декоратора IoC-контейнером инжектируются SEModel и CFProvider. Декоратор для обернутой модели устанавливает режим синхронного обновления и подписывается на событие изменения свойств.
Основная работа выполняется в методе DoUpdate. Декоратор сначала обновляет custom поля для индексированной по Id сущности, а затем делегирует обновление обернутой DataModel.
Единственное место, где к декоратору обращаются явно - custom xaml, который хранится в БД и создается в конфигураторе. В данном примере его будет поставлять ICustomContentModel. Xaml как я уже говорил отображается XamlViewer:
, а сам xaml может выглядеть вот так:
Все метаданные (список custom полей, xaml с данными - имя поля к которому надо привязываться (binding), порядок расположения полей, их имена в UI, отображаени/скрытие выбранных полей) конфигурируются в специальном приложении - Configurator.
Удачи.
Комментариев нет:
Отправить комментарий