вторник, 17 марта 2009 г.

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.