Я рефакторинг нашего приложения, чтобы включить Dependency Injection (через инъекцию конструктора) и работать в сложный краеугольном случае:Создания экземпляров плагина с помощью контекста DI
В настоящее время мы имеем ImageViewer
объектов, когда экземпляр поиска сборки для экземпляров ImageViewerPlugin
(абстрактного базового класса), создавая их с помощью отражения. Это делается в конструкторе ImageViewer
, используя метод (называемый в цикле для всех конкретных типов подключаемых модулей), который похож на:
private ImageViewerPlugin LoadPlugin(Type concretePluginType)
{
var pluginConstructor = concretePluginType.GetConstructor(
BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public,
null,
new[] { typeof(ImageViewer) },
null);
return (ImageViewerPlugin) pluginConstructor.Invoke(
new object[] { constructorParameter });
}
Класс ImageViewerPlugin выглядит примерно так:
internal ImageViewerPlugin
{
protected ImageViewer _viewer;
protected ImageViewerPlugin(ImageViewer viewer)
{
_viewer = viewer;
}
}
конкретная реализация выглядит примерно так:
internal AnImageViewerPlugin
{
public AnImageViewerPlugin(ImageViewer viewer) : base(viewer)
{
}
}
Каждый ImageViewer
экземпляр имеет свою собственную коллекцию ImageViewerPlugin
инст Ances.
Теперь, когда приложение реорганизовано для использования контейнера DI и инсталляции конструктора, я обнаруживаю, что эти плагины имеют зависимости (которые ранее были скрыты с использованием глобальных статических классов), которые должны быть разрешены контейнером DI, но я не уверен, как это сделать, не используя Service Locator (анти-шаблон).
Наиболее разумным решением является создание этих экземпляров плагина с использованием DI. Это позволило бы мне добавить дополнительные параметры конструктора, чтобы они зависели от инъецируемых через инсталляцию конструктора. Но если я это сделаю, как я могу передать конкретное значение параметра viewer
, а остальные значения введенного значения будут введены?
Я думал, что ImageViewerPluginFactory
поможет достичь этого, но не может понять, как реализовать такой завод, поскольку каждый плагин, вероятно, будет иметь другую подпись конструктора.
Как я могу решить этот сценарий? Или я подхожу к этому совершенно неправильно?
Спасибо, Стивен. Я обновил свой вопрос, чтобы показать, как я создаю экземпляры плагина. Я уже подумал о переходе на описанный вами проект с использованием метода RegisterInitializer, однако я считаю, что он вполне действителен для того, чтобы иметь базовый класс плагина, который, как ожидается, получит свой контекст через параметр конструктора. В противном случае чувствуется как (хотя и небольшой) взлом. Обычно плагин запускается в контексте всего приложения, и в этом случае его контекст является неявным. В этом случае он имеет гораздо более ограниченный контекст, поэтому его нужно рассказать, каков его контекст. – Josh
Я посмотрел ваше обновление, но мой совет тот же. Не допускайте смешивания этой циклической ссылки с другими зависимостями в конструкторе. На этом есть [умные «хаки»] (http://stackoverflow.com/questions/15123515/pass-runtime-value-to-constructor-using-simpleinjector), но вместо этого: держите его простым, держите его быстрым. – Steven
Спасибо за ответ. Хотя для меня это не идеально, я собираюсь взять ваш совет и пойти с методом инициализации. – Josh