Я поделил свою проблему на короткую и длинную версию для людей, у которых мало времени.
Короткая версия:
Мне нужна архитектура для системы с плагинами провайдера и потребителя.
Поставщики должны внедрить интерфейс IProvider, а потребители - IConsumer.
Исполняющее приложение должно знать только о IProvider и IConsumer.
Реализация потребителя может запросить исполняющую сборку (посредством ServiceProcessor), какие провайдеры реализуют InterfaceX, и возвращает список обратно.
Эти объекты IProvider должны быть преобразованы в InterfaceX (у потребителя), чтобы иметь возможность подключить потребителя к некоторым событиям, которые определяет InterfaceX. Это не удастся, потому что исполняющая сборка как-то не знает этот тип InterfaceX (приведение не выполняется) Решение состоит в том, чтобы включить InterfaceX в какую-то сборку, на которую ссылаются как плагины, так и исполняемая сборка, но это должно означать перекомпиляцию для каждой новой пары поставщик / потребитель, что крайне нежелательно.
Есть предложения?
Длинная версия:
Я разрабатываю некий общий сервис, который будет использовать плагины для достижения более высокого уровня повторного использования. Служба состоит из некоторой реализации шаблона Observer с использованием провайдеров и потребителей. И поставщики, и потребители должны быть плагинами для основного приложения. Позвольте мне сначала объяснить, как работает сервис, перечислив проекты, которые у меня есть в моем решении.
Проект A: Проект службы Windows для размещения всех плагинов и основных функций. Проект TestGUI Windows Forms используется для облегчения отладки. Экземпляр класса ServiceProcessor из Project B выполняет связанные с плагином вещи. Подпапки «Потребители» и «Поставщики» этого проекта содержат подпапки, где каждая подпапка содержит подключаемый модуль потребителя или поставщика соответственно.
Проект B: Библиотека классов, содержащая класс ServiceProcessor (который выполняет всю загрузку и диспетчеризацию плагинов между плагинами и т. Д.), IConsumer и IProvider.
Проект C: библиотека классов, связанная с проектом B, состоящая из TestConsumer (реализующий IConsumer) и TestProvider (реализующий IProvider). Дополнительный интерфейс (ITest, сам по себе производный от IProvider) реализован в TestProvider.
Цель в том, чтобы плагин Consumer мог спросить у ServiceProcessor, какие у него есть провайдеры (реализующие хотя бы IProvider). Возвращенные объекты IProvider должны быть преобразованы в другой интерфейс, который он реализует (ITest) в реализации IConsumer, чтобы потребитель мог подключить обработчики событий к событиям ITest.
При запуске проекта A загружаются подпапки, содержащие плагины потребителя и поставщика. Ниже приведены некоторые проблемы, с которыми я столкнулся и пытался их решить.
Интерфейс ITest, используемый для размещения в Project C, поскольку это относится только к методам и событиям, о которых известно TestProvider и TestConsumer. Основная идея - сохранить проект простым и не знать, что плагины делают друг с другом.
С ITest в проекте C и кодом в методе Initialize TestConsumer, который преобразует IProvider в ITest (это не могло бы привести к сбою в самой библиотеке отдельных классов, когда объект, реализующий ITest, известен как объект IConsumer), неверное приведение ошибка произойдет. Эту ошибку можно устранить, поместив интерфейс ITest в проект B, на который также ссылается проект A. Это крайне нежелательно, поскольку нам нужно перекомпилировать проект A при сборке нового интерфейса.
Я попытался поместить ITest в единую библиотеку классов, на которую ссылается только проект C, поскольку только поставщик и потребитель должны знать об этом интерфейсе, но безуспешно: при загрузке плагина CLR заявляет, что указанный проект не может быть найденным. Эту проблему можно решить, подключив событие AssemblyResolve текущего AppDomain, но почему-то это тоже кажется нежелательным. ITest снова вернулся в проект B.
Я попытался разделить проект C на отдельный проект для потребителя и поставщика и загрузить обе сборки, которые сами по себе работают хорошо: обе сборки находятся в коллекции Assemblies или текущем домене приложений:
Обнаружена сборка: Datamex.Projects.Polaris.Testing.Providers, версия = 1.0.0.0, культура = нейтральная, PublicKeyToken = 2813de212e2efcd3
Обнаружена сборка: Datamex.Projects.Polaris.Testing.Consumers, версия = 1.0.0.0, культура = нейтральная, PublicKeyToken = ea5901de8cdcb258
Поскольку Потребитель использует Поставщика, от Потребителя была сделана ссылка на Поставщика. Теперь событие AssemblyResolve сработало снова, заявив, что ему нужен следующий файл:
AssemblyName = Datamex.Projects.Polaris.Testing.Providers, версия = 1.0.0.0, культура = нейтральная, PublicKeyToken = 2813de212e2efcd3
Мои вопросы:
Почему это? Этот файл уже загружен правильно?
Почему приведение из IProvider к какому-либо интерфейсу, который, как я знаю, реализуется, невозможно? Возможно, это связано с тем, что исполняющая программа сама не знает этого интерфейса, но не может ли она быть загружена динамически?
Моя конечная цель:
Потребительские плагины спрашивают у ServiceProcessor, какие у него есть провайдеры, которые реализуют интерфейс x. Провайдеры могут быть приведены к этому интерфейсу x без выполнения сборки, зная об интерфейсе x.
Кто-нибудь, кто может помочь?
Заранее спасибо,
Erik