I'm building a WPF (desktop) application, utilizing dependency injection, a DI container and the Register-Resolve-Release pattern. My application also loads plugins from separate assemblies during startup, and the plugins are registered with the DI-container. I resolve my entire object graph once in the composition root at startup, however, I'm having some issues with resolving my plugins, and I'm wondering whether it is OK to inject the DI container into a factory to resolve unknown types?.
My plugins all:
- implement a common interface,
IPlugin - need to be transient (since multiple instances can live simultaneously)
- are dependent on runtime values to configure them when they are being initialized
- might, or might not, have other dependencies injected through their constructor
I have started to implement a PluginFactory to initialize the plugins when I need them, since, as Mark Seemann says: "Any place where you need a run-time value to construct a particular dependency, Abstract Factory is the solution."
However, I cannot just new() the plugins in my factory, since I don't know their types and dependencies.
I have thought of several solutions:
I can inject the DI container into the constructor of the
PluginFactory, resolve the plugins at runtime and call it a day. However, that goes against the RRR-pattern.I can inject
IEnumerable<IPlugin> pluginsthrough the constructor of thePluginFactory. However, this would violate the transient requirement, since I would only have one copy of each instance[*Wrong]. I can solve this by implementingICloneableon all my plugins and clone them in the factory, however, this is both hard to do correct with dependencies and clutters all the plugins.
[*Wrong] Edit: Note, according to the selected answer, this is wrong! Therefore, option #2 is the best option, as long as you register the lifetime of the plugins to be transient.
I can inject the plugin types rather than instances into the
PluginFactoryand use theActivator.CreateInstance<T>()to create the instances. I could even define a common constructor signature for the plugins and pass parameters to the Activator to initialize them with dependencies. However, this violates my requirement that the plugins have different dependencies, and it also leads to the constrained construction anti-pattern.Finally, building on 3. I can inject plugin types and use reflection to sort out the dependencies of the plugins. However, this sounds like it is a lot of work and makes me wonder whether I'm trying to build or copy the resolve methodology from DI containers. The latter is something I definitely don't want to do or see the point with.
So, out of my alternatives in prefer #1, even though that goes against the RRR pattern.
What would be the best way to instantiate my plugins (I'm leaning towards #1). Have I missed some alternatives entirely? Or have I perhaps misjudged some of my other alternatives?
In Simple Injector, injected
IEnumerable<T>instances behave as streams. This means that every time you iterate the enumerable, the container is asked again for an instance. The Simple Injector documentation states:The container will return the instance based on its lifestyle. If the instance is registered as transient, it means that you get a new instance every time you iterate the collection. In a sense, enumerables injected by Simple Injector behave as factories. Here's a little test that shows this behavior:
Some developers are confused by this, but I'd argue that this is the only correct behavior, because
IEnumerable<T>is the definition for a stream, and Simple Injector respects that and, therefore, gives you a stream. There are some interesting advantages in doing this. For instance, it allows enumerables to be injected into singletons, while the lifestyle of the elements is preserved.If you want to register a collection of plugins, this would be the way to do it:
Collection.Registerforwards the resolving of the registered types back to the container and, therefore, respects the lifestyle in which each type is registered. In the example above we didn't register those types separately. This means that all elements are assumed to be transient by default. But you can override this as follows:Now the first plugin in the collection is registered as singleton, while the others are still transient. If you want to register them all with the same lifestyle, you can do the following:
Another options is to pass on a collection of
Registrationinstances into theCollection.Registermethod:In this case, there's no practical difference between the previous registration, but registering
Registrationinstances becomes interesting when you want to do more advanced things such as sharing the same registration instance over multiple abstractions.But the stream behavior can be very useful in your factory, because you can simply inject an
IEnumerable<IPlugin>into your factory and the lifestyle will preserved:The only thing to watch here is that, if you are interested in returning one plugin at a time by filtering over the collection, iterating the collection will produce many plugin instances, even though you might only be interested in one. In the example above,
Singlewill cause all plugins to be created, while you only return one. More efficient would be to doFirst, because that stops when a plugin matches the criteria and this prevents all later plugins from being created.Simple Injector is really fast, so the collection can become quite big before this typically becomes a problem, but it is something to watch out for. If your intention is to filter the list and always select one, a different approach might be better.