WPF and .NET Core 3.0. Dependency Injection

In the upcoming release of .NET Core 3 (planned somewhere around september 2019) we will also have the opportunity to create windows desktop apps with .NET Core. Time to play around with WPF and .NET Core ! So much stuff to try, configuration, Dependency injection, Logging,...

Let's start with Dependency Injection.

In ASP.NET Core you set up dependency injection in the StartUp class. In WPF we do can do it in the App.xaml.cs file. An override of OnStartUp does the trick. For the test I created a MainWindow that needs some input (some simple Info class) through the constructor. So we get code like this:

protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);
    var services = new ServiceCollection();
    services.AddScoped<IInfo,Info>();
}

Alas, that's not enough. This would suffice for an MVC app, for injecting stuff into my Controller. The magic sits in the services.AddMvc(), that sets up the necessary stuff for Dependency Injection to work. For WPF you will also have to add the MainWindow itself into the IoC chain. So the code needs to be like this:

protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);
    var services = new ServiceCollection();
    services.AddScoped<IInfo,Info>();
    services.AddScoped<MainWindow>();

    serviceProvider = services.BuildServiceProvider();
    var mainWindow = serviceProvider.GetService<MainWindow>();

    MainWindow.Show();
}

And there you go: the MainWindow gets an instance of Info. Let's add a button that opens a new instance.

private void Button_Click(object sender, RoutedEventArgs e)
{
    var window = App.serviceProvider.GetService<MainWindow>();
    window.Show();
}

This is interesting to experiment with. If you click the button, nothing happens. Why ? The MainWindow has been registered with AddScoped. This means that as long as we're running in the same scope, we'll get the same instance. So, in our case, the GetService gets the same instance as the window we already have, so calling Show on it doesn't do anything because it's already shown. Registering the Window with AddSingleton gives the same behavior. Changing it to AddTransient gives us a new instance everytime. This will make the button create a new MainWindow every time we click it.

About these different ways of adding something into the IoC chain. AddTransient will give us a new instance of an object every time we need it. AddSingleton always gives us the same instance. AddScoped gives us the same instance, as long as we're working in the same scope. When creating an ASP.NET web application this means we will always get the same instance during one request. In WPF by default you have a scope as long as your app runs. This means that AddSingleton and AddScoped gives the same result. But, we can make our own scopes !

var scope = serviceProvider.CreateScope();
IServiceProvider prov = scope.ServiceProvider;
var info= prov.GetService<IInfo>();

Great!