MEF II

Продолжаем разговор о MEF и сегодня на повестке дня небольшие дополнения о экспорте/импорте, время жизни дополнений, способы создания дополнений, наследование дополнений.

Экспорт/импорт по контракту

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

С помощью контрактов, можно назначать дополнения на конкретные, заранее определенные поля, без привлечения фильтров и воборок в ручную.

Итак, пусть у нас есть все те же два дополнения. Теперь только мы объявим контракты к ним. Это делается очень просто:

После это в мастер-приложении надо будет только указать поля, помеченные как импорт определенных контрактов.  Конечно про композицию не стоит забывать тоже.

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

 

Область видимости дополнений/время жизни

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

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

С помощью атрибутов мы можем самостоятельно задать время жизни дополнения.

Существует три возможных состояния управления:

  • Any — по умолчанию. Сразу может быть как Shared, так и NonShared
  • Shared – используется один экземпляр экспорта, для всех импортируемых элементов в рамках одного контейнера.
  • NonShared – на каждый запрос импорта создается новый экземпляр экспортируемого типа.

 

Part.Any Part.Shared Part.NonShared
Import.Any Shared Shared Non Shared
Import.Shared Shared Shared No Match
Import.NonShared Non Shared No Match Non Shared

 

Лучше всего продемонстрировать это на примере.

Итак, у нас есть все те же классы для экспорта. Модифицируем их для демонстрации работы обсуждаемого атрибута. Один из классов пометим как общий ресурс, другой как индивидуальный.

Для визуализации пусть в теле класса инициализируется и обновляется сообщение, которое мы увидим в итоге.

Необходимо внести изменения и в импорт дополнений в мастер-приложении. Сделаем дубликаты полей, чтобы стало примерно так:

Получается, что разные свойства импортируют один и тот же контракт и с помощью политики создания мы указали, каким образом создавать экспортируемый элемент. По идее Ru1 и Ru2 должны обращаться к одному и тому же классу совместно. En1 и En2 получили по индивидуальному экземпляру дополнения.

Проверим на практике:

Получим вполне ожидаемый результат:

 

Наследование дополнений

Как вы поняли из подзаголовка MEF поддерживает наследование. Наследование возможно как для импорта, так и для экспорта.

Наследование по аттрибуту Import всегда осуществляется автоматически, для наследования по аттрибуту Export надо это указать явно. Экспорт наследования на уровне полей класса не поддерживается.

Теперь немного поподробнее рассмотрим предыдущий абзац.

Итак, наледование по атрибуту экспорт. Пусть у нас есть такие классы:

Класс RuHello2 не будет найден контейнером и не будет импортирован в программу. Для проверки в основной программе используем коллекцию с аттрибутом ImportMany и вызовем метод SayHello для каждого найденного класса. Получаем:

Т.е. экспорта класса RuHello2 не произошло.

Теперь заменим аттрибут Export на ExportInheritance .

Теперь можно увидеть, что нашлись оба класса.

Наследование полей классов не осуществляется. Т.е.

Покажет только 3 (три) раза слово «Привет».

Метадата при экспорте тоже наследуется. Но если в наследуемом классе переопределить хотя бы одно поле метадаты, то только оно и останется.

Классы и поля с аттрибутом Import всегда наследуются.

Рекомпозиция

С помощью рекомпозиции можно обновлять коллекции и отдельные элементы помеченные для импорта во время выполнения приложения. Можно назвать это даже онлайн импортом. Например можно отслеживать папку с плагинами и при появлении новых файлов переинициализировать каталог MEF с дополнениями. Очень удобно может быть.

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

AllowRecomposition = true

 

 

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

Конструкторы

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

Для того, чтобы однозначно указать какой конструктор использовать, воспользуемся аттрибутом ImportingConstructor. Если аттрибут использовать более одного раза в классе, MEF выдаст ошибку во время исполнения программы.

Итак, пусть у нас есть все тот же наш класс RuHello, только теперь он будет принимать в конструктор класс описывающий пользователя.

Класс Name помечается на экспорт, а в конструкторе класса RuHello он помечается для импорта. На самом деле аттрибут можно опустить в данном случае и все будет работать. Но для более сложных случаев, можно будет явно указать что импортировать.

Самое главное, конструктор помечен аттрибутом ImportingConstructor. MEF способен разрешать довольно длинные зависимости классов и корректно их обрабатывать.

Вывод на консоль будет таким:

 

И еще немного аттрибутов

Скрытие обнаружение

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

Опциональность импорта

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

И так можно сделать с помощью параметра AllowDefault. Он говорит MEF, что если ничего не найдено для импорта, то можно вставить значение по умолчанию, для классов это будет null.

На этом пока что все ))

 

Hard’n’heavy!

 

 

 

 

Оставить комментарий