System.Addin или «Игры с надёжными плагинами». Часть 1

    Введение.

    Доброго времени суток. Я думаю, что абсолютное большинство из вас сталкивалось с проблемой расширяемости приложений. Точно также я думаю, что многим из вас приходилось копать Reflection для выяснения того, является ли сборка плагином к вашей программе. Многим не нравилось то, что в .NET сборки по умолчанию загружаются в один домен с приложением, а затем их нельзя было выгрузить. Многие, конечно, создавали объекты в отдельных доменах через CreateInstanceAndUnwrap, но всё это приходилось делать руками. В общем «мыши плакали и кололись…». С появлением System.Addin разработчики получили в свои руки инструмент для создания расширяемого приложения, который лишён этих проблем, что называется, «из коробки». Об этой технологии я и расскажу в нескольких статьях.

    Прежде, чем начать – разберёмся с терминологией:
    Хост, Хост-приложение – это, собственно, расширяемое приложение, которое, как правило, производит поиск и активацию расширений.
    Addin, Расширение – это модуль, который содержит какой-либо дополнительный функционал для хост-приложения

    Возможности System.Addin

    Что же такое System.Addin? Это новое пространство имён(namespace), которое появилось в .NET Framework 3.5. По сути своей System.Addin предоставляет разработчикам программную модель для расширения функционала приложения. Причём применение этой программной модели даёт несколько ключевых возможностей:

    1. Хост-приложение и Addin могут иметь независимые версии. Таким образом можно построить хост-приложение, которое бы работало и со сборками расширения, которые были построены для предыдущих версий приложения, либо для более поздних версий приложения, либо которые вообще были построены для другого приложения.

    2. Возможность активировать Addin с нужным уровнем изоляции и правами безопасности. То есть программная модель System.Addin позволяет позаботиться о безопасности приложения даже если Addin был создан сторонними разработчиками. Теперь никакой бажный модуль расширения не завалит вашу программу.

    3. Поддержка нескольких уровней изоляции расширения от хост-приложения и других расширений. То есть существует несколько возможных сценариев построения изоляции на уровне доменов или процессов, за счёт чего усиливается безопасность хост-приложения.

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

    Окей. С некоторыми возможностями разобрались. Идём дальше.

    Архитектура

    Вся архитектура System.Addin строится вокруг такого ключевого понятия, как Add-in pipeline(Я бы перевёл как «конвейер расширений»). Под этим словосочетанием по сути всё и скрывается. Смотрим следующий рисунок, всё с архитектурой проясняющий:

    image


    Это — тот самый Add-in pipeline. За счёт всех этих сегментов в пайплайне и достигается необходимый уровень абстракции и обеспечивается изоляция. Разберём поподробнее. На концах конвейера видим Хост-приложение и само расширение. Это не самые интересные вещи, поэтому сразу перейдём к рассмотрению того, что находится между ними:

    1. Контракт. Как видно из рисунка, контракт представляет собой интерфейс, «протокол взаимодействия» хоста и расширения, и является точкой их соприкосновения.

    2. Далее по обе стороны контракта создаются адаптеры (наследуют Views), которые реализуют соответствующие классы представления(views) и оборачивают контракт интерфейсом, который предоставляет view. Также, если вы не объединяете одной сборкой Addin view и Host view, вы можете объединить Host и Host view в одну сборку. В любом случае мой вам совет: используйте отдельную сборку для каждого сегмента конвейера. Так будет проще.

    3. Ну и третьим пунктом идут классы-представления (строго должны быть интерфейсами, либо наследуемыми классами). Они являются соответствующими представлениями типов и методов, которые используются при взаимодействии хоста и расширения.

    На данном этапе этой информации нам хватит. Достаточно просто представлять себе как выглядит Add-in pipeline. Более подробно роль каждого из сегментов этого конвейера мы рассмотрим в одной из следующих статей.
    Стоит также отметить, что архитектура накладывает некоторые ограничения, с которыми мы сталкиваемся в процессе создания:

    1. Во-первых, каждый сегмент из конвейера в идеале представляет собой отдельную сборку. Представления можно объединить в одну сборку, но только при условии такого же объединения адаптеров.

    2. Во-вторых, Addin-ы, адаптеры и контракты должны быть публичны и помечены специальными атрибутами. См. рисунок ниже:
    image


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

    3. В-третьих, требуется соблюдение жёсткой структуры папок, которая должна строго соблюдаться для нормального функционирования конвейера:

    image


    *Addin-ы не обязательно лежат рядом с адаптерами, представлениями и контрактами. Расширения могут распологаться где угодно.
    Можно заметить, что каждый сегмент конвейера должен быть расположен в своей собственной папке, причём папки AddInSideAdapters, AddInViews, Contracts и HostSideAdapters не допускают наличия в них вложенных директорий.

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

    Вот так. Ни больше, ни меньше. Архитектура и накладываемые ограничения могут на первый взгляд показаться излишне сложными, но в реальности это не так. Я думаю, что теперь многие из вас представляют себе, что же такое System.Addin
    На этом всё.

    В следующей статье я покажу пример, демонстрирующий применение System.Addin.

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 53

      +3
      Рисунков нет. :(
        0
        Сорри… поправил… сейчас вроде как должно быть всё хорошо.
          0
          Опять нет рисунков. Перезалейте на habrastorage.
        +1
        Спасибо. Как раз некоторое время назад начал изучать System.Addin. Единственное что не нравится — это необходимость жёстко задавать структуру директорий. Я так понимаю, что проблема в том, что в классе-хелпере AddInStore нет возможности изменять названия директорий. Интересно было бы увидеть пример загрузки аддинов без использования AddInStore, если это вообще возможно.
          0
          #develop? Его архитектура вообще не предполагает полезного функционала вне addin'ов :)
            +1
            Почему же не предполагает?) Хост-приложение может быть обычным Stand-alone приложением :) Может так же по-старому цеплять плагины через Reflection или вообще работать без всяких расширений :)
              0
              Хост у них и так standalone. Был в версии 1.0, по крайней мере :) Тем не менее, функционал вне addin'ов у них считается (считался?) дурным тоном.
              Скорее всего, мои познания о внутренностях #develop уже устарели — в той мере, в какой устарела «Dissecting a C# Application: Inside SharpDevelop» :)
            0
            Поковырялся рефлектором и понял что без AddInStore ничего не получится… Жаль.
            0
            Стоп :) Я только сейчас понял, что Вы говорите о SharpDevelop :)
            Статья совсем не о #develop :) Она о новом полезном инструменте для разработчиков, который включён в библиотеку .NET 3.5 ;)
              +1
              Блин… промахнулся с ответом… извиняйте :) Я тут новичок) Не привык ещё)
            • UFO just landed and posted this here
                +2
                Напротив, полезно было узнать про этот блок, при случае разберусь :)
                • UFO just landed and posted this here
                    +2
                    Невероятно, интересная тема и не перевод.
                    Но явно не хватает примеров кода, вместо картинок было бы нагляднее.
                      +1
                      Не хотелось, чтобы статья получилась длинной… следующая будет как раз демонстрировать пример :)
                    • UFO just landed and posted this here
                        0
                        Это часть .NET 3.5, но вынесен из System.Core.dll в отдельную сборку System.AddIn.dll.
                        0
                        Что имеется в виду под представлением? Я про конкретные классы. Это пользовательские интерфейсы?
                          0
                          Это либо интерфейсы, либо какой-либо наследуемый(возможно абстрактный) класс.
                          Так, например, Хост видит Addin только через Хост-представление… хотя фактически работает с объектом адаптером, который перенаправляет запросы к контракту, который в свою очереднь перенаправляет их дальше на сторону расширения.

                          Следующая статья многое прояснит.
                            0
                            Странно, что они контракты представлениями назвали. Ладно, подожду следующей статьи :-D
                              0
                              Ну «представление» — это мой вольный перевод слова view :)
                          0
                          С нетерпением жду следующей статьи. Пока всё выглядит как что-то очень мутное и непонятное, рассчитанное не менее чем на систему управления шаттлами. :)
                            –1
                            Майкрософт всегда так делает.
                              +1
                              Ну, не всегда, и в большинстве случаев всегда можно использовать лишь маленький кусок из того, что тебе нужно.
                              Просто в стандартную библиотеку приходится включать максимально общий и универсальный механизм, иначе обязательно кому-нибудь чего-нибудь не хватит. :)

                              Но тут уж как-то больно заумно на первый взгляд.
                                +1
                                Во всяком случае это не сложнее, чем написать интерфейсы для взаимодействия хоста и add-in'а. Я 3.5 не юзал и вообще как бы не хочу переходить с версии выше 2.0, потому что пора менять технологию на опенсорсную.

                                Но в 2.0 и 1.1 надо было писать интерфейсы, они точно так же считались контрактами, и посредством рефлекшн, активаторов, доменов приложений и прочего, так же с ними работали. Просто сделали обёртку для всего этого, вот и всё.

                                Хотя я не знаю, стоит ли ей пользоваться или писать своё, но шатлы то уж винде точно не доверил бы :)
                                  +1
                                  Если вопрос стоит так, что стоит использовать System.Addin или писать тоже самое, но руками, то тут выбор явно следует сделать в пользу первого. Хотя бы из соображений экономии времени :)
                                    0
                                    Это как сказать, у каждого способа явно есть преимущества и недостатки, от программиста наверное зависит. Надо по идее рефлектором смотреть, что они там наделали в своей новой дллшке, тогда можно оценить более точно, что наиболее оптимально подходит.
                                      +1
                                      А ещё можно просто обратиться к документации и обойтись без копания исходников :)
                                        0
                                        Обращение к MSDN не даёт реальной картины. Технология чёрного ящика и если бы он был идеален. Так ведь нет, там такого порой понаписано.
                                          0
                                          Копание исходников рефлектором — крайность, к которой лично я прибегаю только в том случае, если уже пересмотрена вся документация, а проблема всё ещё не решена… либо когда адекватной доки просто нет.

                                          Я более, чем уверен, что и вы не смотрите реализацию каждого класса рефлектором.
                                          В MSDN есть косяки… но на фоне общего количества информации — эти косяки занимают ничтожно мало места :)
                                            +1
                                            Да, про то, что смотреть в исходники не нужно писали в книге «Совершенный код». Вроде как если видишь реализацию, и используешь её особенности в своём коде, в общем, те кто надо прочтут…

                                            Однако я вот такой вот жуткий маньячище. Сначала я открываю Visual Studio разных версий, потом несколько версий MSDN, и сразу после этого несколько копий рефлектора с набором разных библиотек фреймворков.
                                              0
                                              У каждого свой стиль ;)
                                              0
                                              В рефлекторе все равно быстрее, там как правило ты видишь 2-5 сстрочек, и тебе все ясно. А в MSDN у них примеров мало и поэтому втухаешь над каждым абзацем, как над докой по шатлу.
                                                +1
                                                На вкус и цвет, опять же… лично я сомневаюсь в эффективности разработки с одним только рефлектором под руками и без MSDN…
                              +2
                              На самом деле, 9/10 кода генерируется с помощью тулзы PipelineBuilder, которая к тому же умеет встраиваться в VS как аддин. Достаточно создать только одну сборку, содержащую контракты. Потом сбилдить её и натравить на получившийся dll-файл PipelineBuilder, после чего мы получим 4 готовые сборки, содержащие представления и адаптеры для обеих сторон взаимодействия.
                                0
                                Именно так :)
                                +1
                                Managed Extensibility Framework у них поинтереснее будет, как мне кажется. VS10 его будет использовать для расширяемости.
                                  0
                                  На самом деле публикация нескольких статей по MEF была у меня в планах после завершения цикла статей про System.Addin :)
                                    0
                                    Это что, история Linq2Sql -> Entity Framwork повторяется? Сначала сделаем быстро и эффективно, а потом заумно и тормозно.
                                      0
                                      Не смотря на то, что MEF и System.Addin вроде бы как служат для одной цели — они разные. Просто разные. У каждого из них есть свои фишки, которых нет у другого…
                                    0
                                    Более подробно на сайте производителя:)
                                    http://msdn.microsoft.com/ru-ru/library/bb384241.aspx
                                      0
                                      Пробовал пользоваться… запутано все ужасно. Когда разбирался, чуть клавиатуру не съел…

                                      Решили, что проще и намного удобнее написать свое решение в две строчки и не парить мозг)
                                        0
                                        Если не секрет, какими возможностями обладает «решение в две строчки»? :)
                                          0
                                          Одной. Умеет загружать длл'ки из папки плагинс. То, что нужно.
                                          Помнится, когда пробовал сабж, в солюшене было с полдесятка абсолютно левых проектов с либами каких-то контрактов, адаптеров… Так нельзя делать по-моему.
                                            0
                                            System.Addin разрабатывался для несколько других сценариев…
                                        0
                                        Жаль OSGI стандарт и систему плагинов а-ля Eclipse портануть под .Net нельзя
                                          0
                                          Опыт показывает, что возможно всё.
                                          Вопрос только в том, сколько времени это займёт :)
                                            0
                                            там концептуальная проблема…

                                            — Сборки загруженные в одном домене выгружать нельзя.
                                            — Загружая в разные домены имеем нехилый оверхед на ремоутинг.
                                              0
                                              в блоге команды CLR Addins Джесс Каплан, если мне память не изменяет, писал статейку, в которой делался вывод о том, что оверхед не такой уж и страшный при использовании System.Addin. In-domain, Cross-domain и Cross-process сценарии сравнивались.
                                          0
                                          Насколько я понимаю, последующих статей ожидать не стоит?
                                            0
                                            Не стоит. Используйте MEF и будет счастье. System.Addin, я надеюсь, уйдёт в анналы истории и больше никогда оттуда не вернётся :)
                                              0
                                              Это зря! MEF и MAF — два разных подхода. MEF не позволяет получить изоляцию (использование AppDomain), а именно это мне и надо и именно это хорошо умеет MAF.
                                              ystem.Addin is a great technology for addressing issues around versioning resliance, isolation and recoverability.
                                              • Using System.Addin allows you to host different components in separate app domains, thus allowing those addins to have different versions of assemblies which they reference which all run in the same process.
                                              • System.Addin allows you to separately version Addins so that you could have two versions of an Addin sitting side by side.
                                              • System.Addin manages automatically unloading app-domains they are no longer used thus allowing you reclaim memory.
                                              • System.Addin has a bunch of security infrastructure (addins run in a sandbox) to ensure that components that are loaded do not have unauthorized access to data in the rest of the app.
                                              • System.AddIn allows your app to gracefully recover whenever one of the addins crashes (this is due to isolation)

                                              MEF on the other hand is a great technology for discovery and composition
                                              • MEF composes deep object hierarchies of components
                                              • MEF abstracts components from static dependencies.
                                              • MEF can allow components to be lazily loaded and lazily instantiated.
                                              • MEF provides a cataloging mechanism and rich metadata for components to allow them to dynamically discovered.
                                              • MEF can compose components from various programming models, it is not bound to static types.


                                              Зачем вообще тогда было писать первую статью?
                                                0
                                                Выше в каментах я уже писал о том, что это два разных подхода. Тут вопросов нет.
                                                По личному опыту — случаев, когда необходимо использовать System.Addin — мало. Геморроя связанного со спецификой AppDomain — много, но иногда он оправдан.

                                                Посему… начав писать демо для второй статьи, и посчитав впоследствии это всё неинтересным, я забил на неё…

                                          Only users with full accounts can post comments. Log in, please.