I'm refactoring our application to include Dependency Injection (via constructor injection) and have run into a tricky corner-case:
We currently have ImageViewer
objects that when instantiated search the assembly for ImageViewerPlugin
(an abstract base class) instances, instantiating them using reflection. This is done in the constructor of the ImageViewer
using a method (called in a loop for all concrete plugin types) that is similar to:
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 });
}
The ImageViewerPlugin class looks roughly like this:
internal ImageViewerPlugin
{
protected ImageViewer _viewer;
protected ImageViewerPlugin(ImageViewer viewer)
{
_viewer = viewer;
}
}
A concrete implementation looks roughly like this:
internal AnImageViewerPlugin
{
public AnImageViewerPlugin(ImageViewer viewer) : base(viewer)
{
}
}
Each ImageViewer
instance has its own collection of ImageViewerPlugin
instances.
Now that the application being refactored to use a DI container and constructor injection I'm discovering that these plugins have dependencies (which were previously hidden through the use of global static classes) that need to be resolved by the DI container, but I'm not sure how to do that without using a Service Locator (an anti-pattern).
The most sensible solution seems to be to create these plugin instances using DI. This would allow me to add extra constructor parameters to have the dependencies they rely on injected via constructor injection. But if I do that, how can I pass a specific viewer
parameter value while having the rest of the parameter values injected?
I thought an ImageViewerPluginFactory
would help to achieve this but can't see how to implement such a factory as each plugin is likely to have a different constructor signature.
How can I solve this scenario? Or am I approaching this totally the wrong way?