Представляем preview MEF.DEV Platform

Облачная платформа MEF.DEV предоставляет возможности ускорения разработки, хостинга и управления использованием приложений для независимых разработчиков и компаний-разработчиков на основе Managed Extensibility Framework с помощью гибкого процесса разработки на принципах непрерывной интеграции. Она имеет удобный интерфейс, который упрощает процесс разработки интеграционных приложений с хорошим уровнем соглашения об обслуживании и на основе утвержденных на предприятии стандартов авторизации, аутентификации, управляемого развертывания со стандартизированными подходами к мониторингу и SOX-контролям.

К базовым функциям платформы относятся процессы, требующие стандартизации в рамках процесса разработки програмного обеспечения конкретного предприятия – тем самым устанавливая «из коробки» стандартные подходя для ряда важных фаз SDLC процесса:

REST API платформы в качестве основного элемента решения расширяет функциональность доступных в ней плагинов, например BSS системы, предоставляя доступ к ее ресурсам, таким как личные счета, абоненты или услуги, используя уникальные значения URI для внешнего взаимодействия или названия сущностей в рамках одного домена - для пере-использования (внутреннего взаимодействия). Чтобы использовать REST API, достаточно отправить HTTP запрос и проанализировать ответ. REST API использует JSON или XML в качестве формата обмена в сочетании со стандартными методами HTTP GET, PUT AND POST:

  • GET возвращает данные. Этот метод не меняет данные, хранящиеся на сервере
  • PUT изменяет существующую запись данных по ID и полной модели данных
  • POST создает новую запись данных
  • DELETE удаляет запись данных по ID

Сущности как уникальные адресаты

Уникальные значения URI автоматически отображаются платформой UCP после публикации определенной версии моделей данных (сущностей). Сущности обеспечивают доступ к бизнес-логике и конкретному (нативному) хранению данных с помощью Managed Extensibility Framework (MEF). MEF гораздо больше, чем внедрение зависимостей (Dependency injection) или инверсия управления (Inversion of Control) - это дает Enterprise-разработчикам более простую настройку и последовательное управление в более крупных проектах, не заботясь о том, когда, где и как настроено конкретное приложение/блок/модуль, и независимо от того, как они используют внедренное приложение/блок/модуль - получая именно ту реализацию, которую они хотят. MEF разработан, чтобы помочь разработчикам связать и использовать то, что они не знают. Он расширяется по своей природе и содержит функции стабильной экпозиции, которые обеспечивают безопасность расширяющихся Enterprise-проектов в реальном режиме времени. Другими словами, после того, как один разработчик запускает приложение и загружает его на UCP платформу, другие разработчики, создающие другие плагины, могут использовать данный плагин или его новую версию в реальном режиме времени без перезагрузки – т.е. «на лету».

Визуализация содержимого конкретного плагина (фактически реализации конкретного разработчика) дает прозрачность для всех остальных участников платформы, например визуализация плагина Natec.Entities для домена BSS выглядит следующим образом:

Формат OpenAPI

Мы хотим улучшить Ваш рабочий процесс разработки интеграции с помощью нашего REST API – по этой причине мы создали инструмент для гибкого и непрерывного процесса интеграции для корпоративного SDLC процесса. На данный момент мы предлагаем общий плагин (это всего лишь один файл), который можно использовать в процессе самостоятельной разработки приложений - Вы можете получить также «из коробки» автоматически сгенерированную документацию для созданных Вами REST сущностей на основе атрибутов-декораторов.

Вы можете посмотреть примеры плагинов в нашем репозитории GitHub, также доступны видео-уроки для разработчиков доступны по ссылке

Назначение MEF

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

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

В идеальной перспективе, разработчик, который изучил MEF, будет способен (без особых сложностей и длительного изучения архитектуры) разрабатывать компоненты для всевозможных проектов на платформе .NET, написанных любыми другими компаниями или отдельными людьми. И, таким образом, MEF способен решить задачу взаимопонимания между разработчиками, предлагая общий язык общения.

Основы

Залог состоявшейся успешности MEF как инструмента кроется в его простоте. MEF построен всего на трех функциональных частях: импорт, экспорт и композиция. Используя импорт вы характеризуете части вашего приложения как способные к расширяемости. Сторонний разработчик, используя функции экспорта, создает отдельный компонент (часть, плагин), предназначенный для вашего приложения. И, в ходе выполнения, вы используете функции композиции для того чтобы соединить части импорта с частями экспорта. Рассмотрим эти этапы подробнее.

Импорт

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

Здесь определяется автоматическое свойство, тип которого определяется неким интерфейсом IPlugin. С помощью атрибута Import, который является частью инфраструктуры MEF, свойство помечается как импортируемое. Само свойство таким образом становится частью импорта, а типом части будет являться интерфейс IPlugin.

Обратите внимание на параметр атрибута Import: typeof(IPlugin) в данном случае определяет так называемый контракт MEF. Контактом называется уникальный идентификатор, который однозначно определяет часть иморта, часть экспорта и таким образом позволяет MEF соединить обе части в процессе композиции. Проще говоря, определяя контракт вы сообщаете некий пароль, который должна назвать часть расширения для того, чтобы присоединиться к точке импорта. Далее в этой главе контракты будут рассмотрены более подробно.

Экспорт

Экспортируемая часть

Здесь определяется некий класс FirstPlugin, который реализует интерфейс IPlugin (часть импорта в предыдущей теме определена с помощью него же). С помощью атрибута Export из инфраструктуры MEF класс помечается как экспортируемая часть (можно сказать “плагин”). Обратите внимание, параметром атрибута Export служит контракт объявленный как typeof(IPlugin).

Определение одинакового контракта при импорте и экспорте позволяет MEF находить предназначенный друг-другу части.

Композиция

После определения импортируемых и экспортируемых частей необходимо произвести их композицию

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

Композиция

Здесь создается экземпляр контейнера композиции (контейнер – это часть инфраструктуры MEF). После чего, у контейнера вызывается метод ComposeParts, параметры которого представляют собой перечисление элементов, в которых MEF должен искать части для композиции. В данном случае, this – это экземпляр текущий класса и new FirstPlugin() – это инстанцированный плагин, помеченный нами в предыдущей части атрибутом Export.

После вызова ComposeParts, в контейнере container будут записаны экземпляры this и FirstPlugin, а ипортируемая часть Plugin получит значение экземпляра FirstPlugin. Чуть далее мы рассмотрим весь процесс вместе в примере кода.

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

Контракты

Контракты в инфраструктуре MEF играют важную связующую роль между частями импорта и экспорта. Контракты обязательно явно или неявно определяются при импорте или экспорте частей. На рис. выше в качестве контракта используется выражение typeof(IPlugin), которое уникально определяет тип интерфейса IPlugin.

На самом деле инфраструктура MEF содержит несколько возможностей определение контракта при импорте:

Варианты определения контрактов при импорте

ImportAttribute(Type) с помощью указания передачи типа (так как мы рассматривали)
ImportAttribute(String) с помощью передачи имени контракта в виде строки — в этом случае, вы должны обязательно гарантировать уникальность такой строки среди других контрактов
ImportAttribute(String, Type) с помощью передачи как имени контракта в виде строки, так и его типа – это может оказаться полезным, когда появляется потребность создать несколько разных контрактов для одного и того же типа
ImportAttribute() в случае, если атрибутам импорта (Import и другие) не был передан тип контракта, то он будет определен автоматически на основании типа к которому этот атрибут применяется. Таким образом, вы можете опустить параметр typeof(IPlugin).

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

Для атрибутов экспорта действуют те же правила, что и при импорте. Но при определение экспорта с помощью атрибутов Export и других важно понимать следующее поведение: в случае, если не указан тип и имя контракта они будут получены автоматически на основании типа элемента к которому применяется атрибут. Иными словами, если в примере на рис. “Экспортируемая часть” опустить параметр typeof(IPlugin), то инфраструктура MEF определит контракт автоматически на основании типа FirstPlugin, но не IPlugin, как нам того требуется. Это означает, что если вы строите экспортируемую часть на основе базовых интерфейсов или классов, то вам необходимо явно указывать для контракта его тип.