How to combine designable components with dependency injection
- by Wim Coenen
When creating a designable .NET component, you are required to provide a default constructor. From the IComponent documentation:
To be a component, a class must
implement the IComponent interface and
provide a basic constructor that
requires no parameters or a single
parameter of type IContainer.
This makes it impossible to do dependency injection via constructor arguments. (Extra constructors could be provided, but the designer would ignore them.) Some alternatives we're considering:
Service Locator
Don't use dependency injection, instead use the service locator pattern to acquire dependencies. This seems to be what IComponent.Site.GetService is for. I guess we could create a reusable ISite implementation (ConfigurableServiceLocator?) which can be configured with the necessary dependencies. But how does this work in a designer context?
Dependency Injection via properties
Inject dependencies via properties.
Provide default instances if they are
necessary to show the component in a
designer. Document which properties
need to be injected.
Inject dependencies with an Initialize method
This is much like injection via properties but it keeps the list of dependencies that need to be injected in one place. This way the list of required dependencies is documented implicitly, and the compiler will assists you with errors when the list changes.
Any idea what the best practice is here? How do you do it?
edit: I have removed "(e.g. a WinForms UserControl)" since I intended the question to be about components in general. Components are all about inversion of control (see section 8.3.1 of the UMLv2 specification) so I don't think that "you shouldn't inject any services" is a good answer.
edit 2: It took some playing with WPF and the MVVM pattern to finally "get" Mark's answer. I see now that visual controls are indeed a special case. As for using non-visual components on designer surfaces, I think the .NET component model is fundamentally incompatible with dependency injection. It appears to be designed around the service locator pattern instead. Maybe this will start to change with the infrastructure that was added in .NET 4.0 in the System.ComponentModel.Composition namespace.