Использование абстрактного класса в качестве контракта в рамках плагина
Может ли абстрактный класс использоваться в качестве объекта контракта между 'Host' и 'plugin'? Идея заключается в том, что плагин наследует контракт (мы называем его адаптером). Мы также понимаем, что все участники в рамках должны наследовать MarshalByRefObject
(MBRO). Итак, это то, что мы думали -
Хост:
class Host : MarshalByRefObject
{
}
Контракт:
public abstract class PluginAdapter : MarshalByRefObject
{
}
Плагин:
class myPlugin : PluginAdapter
{
}
Все три существуют в отдельных асмах. Наш Хост создаст новый AppDomain для каждого плагина, а PluginAdapter будет создан следующим образом:
{
ObjectHandle instHandle = Activator.CreateInstance(
newDomain, data.Assembly.FullName, data.EntryPoint.FullName);
PluginAdapter adapter = (PluginAdapter)instHandle.Unwrap();
}
РЕДАКТИРОВАТЬ: где data
это конкретный тип myPlugin
,
Нам было интересно, сработает ли эта реализация структуры. Мы видели статьи, использующие интерфейс (IPlugin) для создания плагинов и конкретный класс в качестве контракта. В этих статьях также говорится, что можно использовать абстрактный класс, но примеры этой реализации не приводятся. Требуется ли, чтобы контракт был конкретным классом?
РЕДАКТИРОВАТЬ: В этом примере Ричард Блеветт - C# Reflection - он использует гораздо более простую реализацию:
Контракт:
public interface IPlugIn
{
// do stuff
}
Плагин:
public class PlugIn : MarshalByRefObject, IPlugIn
{
}
Теперь, если в качестве контракта используется абстрактный класс, плагин не может наследовать как контракт, так и MBRO. Что тогда станет лучшей реализацией для масштабируемой инфраструктуры плагинов. Должны ли мы пойти дальше и внедрить удаленное взаимодействие, хотя изначально мы разрабатываем для работы на одной машине? Ожидается, что этот проект будет распространяться по сети, возможно, через Интернет. Мы просто еще не внедрили Tcp, потому что пытаемся полностью понять основы работоспособности плагина.
Имеет ли смысл реализовать удаленное взаимодействие Tcp на одном компьютере с использованием обратной связи?
4 ответа
Абстрактные классы - лучший выбор для этого, imho. В первую очередь потому, что интерфейсы сложнее для версии. В этом блоге описывается проблема, с которой вы можете столкнуться в будущем, если не будете использовать базовые классы. Это правило относится не только к плагинам, кстати.
О вашем дизайне...
Плагины не должны расширять MBRO. Вы должны использовать свой хост (который должен расширять MBRO), чтобы распределять все вызовы к вашим плагинам, включая обработку событий плагинов. Очень просто непреднамеренно загрузить подключаемую библиотеку DLL в основной домен приложения, если вы попытаетесь перетащить их и использовать их прокси.
Например, если плагин возвращает IEnumerable для одного из своих методов, он может вернуть реализацию IEnumerable, которая определена в сборке плагина. Если это не расширяет MBRO, основной домен приложения должен будет загрузить сборку плагина.
Я загрузил три проекта, связанных с доменами приложений здесь:
Один использует обратные вызовы через домены приложений, второй - обработка событий между доменами, а третий - пример плагина.
В примере плагина приложение определяет интерфейс плагина (это демо, а не лучшие практики!) И хост плагина. Приложение загружает сборку плагина с диска и передает его в домен приложения плагина через прокси хоста плагина, где он загружается. Затем хост плагина создает экземпляр плагина и использует его. Но когда хост возвращает тип, определенный в сборке плагина, обратно в домен приложения, сборка плагина загружается в основной домен приложения, что делает бессмысленным весь плагин.
Лучшее, чего можно избежать, это предоставить абстрактный базовый класс для плагинов, который не помечен как сериализуемый и не расширяет MBRO, а также вернуть только примитивы или запечатанные типы, которые вы определили, обратно через границу из домена плагина.
ПРИМЕЧАНИЕ: все проекты 4.0 RC. Вам нужно это или выше, чтобы запустить их. В противном случае вам придется отредактировать файлы проекта вручную или пересобрать их, чтобы запустить их в версии b2 или 2008.
При условии, что data.EntryPoint.FullName является полным именем типа, приведенный выше код должен работать.
Однако, если вы пытаетесь сохранить этот тип изолированным в своем собственном домене приложений, вам следует быть здесь осторожным. При выполнении data.Assembly
, вы извлечете сборку (и ее типы) в свой домен приложений, в результате чего типы будут загружены в исполняемый домен приложений...
Возможно, вы захотите взглянуть на MAF (Managed Addin Framework), которая является платформой расширяемости, встроенной в.NET для выполнения надстроек. Он аналогичен (и более старый), чем MEF (Managed Extensibility Framework), но имеет больше возможностей для хранения плагинов в собственном домене приложения, среди прочего.
Если data.EntryPoint.FullName
относится к конкретному типу myPlugin
Я не вижу причин, по которым это не сработает (если только не возникают проблемы с загрузкой сборок в другом домене приложения, но это другая проблема).