Description
The text below is copied from my repository README.md where I have built a proof-of-concept implementation of this feature.
The problem described below is probably the most compelling use case for this feature, however I do see other use cases for it as well.
Consider a simple WPF application using the GenericHost
to enable dependency injection following the standard .NET scheme.
The approach is nicely described in this video by @keboo.
This allows us to extract the "top-most" view from the DI container (and thus use a non-default constructor). However, nested controls inside this view are problematic, because they are "instantiated by the XAML parser" and thus require an empty default constructor.
Consider a "top-most" view called MainWindow
using a UserControl
(or a custom control):
MainWindow.xaml
<Window x:Class="SampleApp.MainWindow" ...>
<local:MyUserControl />
</Window>
We now want the nested view (i.e. MyUserControl
) to have a non-default constructor so we can use dependency injection:
MyUserControl.xaml.cs (code-behind)
public class MyUserControl : UserControl
{
private readonly IMessenger _messenger;
public MyUserControl(IMessenger messenger) // This does not work out of the box - default ctor is needed!
{
_messenger = messenger;
}
}
As mentioned, this will not work out of the box, because there needs to be an empty default constructor.
This is just one example of the issue. I could easily imagine other scenarios where this could be useful. For example a "plugin architecture" scenario where plugins are instantiated via reflection using the default constructor.
Proposed solution
The general idea is quite simple:
Use an incremental source generator to create an empty default constructor which in turn invokes the DI-enabled constructor by looking up the dependencies in the DI container.
For this to work, basically 2 things are needed:
- An incremental source generator producing the empty default constructor for (partial) types decorated with a marker attribute.
- A means of getting (static) access to the
IServiceProvider
from the generated constructors in order to lookup the required services.
Desired usage
I want the usage of this to be as simple as possible. Hopefully only 2 steps are needed:
- Decorate the type (e.g.
MyUserControl
) which should be "DI-enabled" with a marker attribute. - Inject the "necessary stuff" into the
IHostBuilder
using a simple extension method.
Ideally something like this:
[InjectDependenciesFromDefaultConstructor] // This instructs the source generator to generate an empty constructor
public class MyUserControl : UserControl
...
using IHost host = CreateHostBuilder(args)
.UseSourceGeneratedDefaultConstructors() // This registers the static access to the IServiceProvider
.Build();