Home > .NET Core, C#, WPF > Update on using HostBuilder, Dependency Injection and Service Provider with .NET Core 3.0 WPF applications

Update on using HostBuilder, Dependency Injection and Service Provider with .NET Core 3.0 WPF applications

07/11/2019

Some months ago we talked about how to use .NET Core 3.0 Dependency Injection and Service Provider with WPF. Now that the final version of .NET Core 3.0 has reached its GA, it’s time to update that article showing the right approach to use HostBuilder, Dependency Injection and Service Provider in WPF applications.

So, after creating a .NET Core WPF application, we need to add the NuGet package Microsoft.Extensions.Hosting to the project. It will allow us to use HostBuilder and, moreover, it automatically imports a bunch of other required packages.

Now, Let’s open the App.xaml file and remove the StartupUri property of the Application class. Then, write the following code in the App.xaml.cs file:

public partial class App : Application
{
    private readonly IHost host;

    public App()
    {
        host = Host.CreateDefaultBuilder()
               .ConfigureServices((context, services) =>
               {
                   ConfigureServices(context.Configuration, services);
               })
               .Build();
    }

    private void ConfigureServices(IConfiguration configuration, 
        IServiceCollection services)
    {
        // ...
        services.AddSingleton<MainWindow>();
    }

    protected override async void OnStartup(StartupEventArgs e)
    {
        await host.StartAsync();

        var mainWindow = host.Services.GetRequiredService<MainWindow>();
        mainWindow.Show();

        base.OnStartup(e);
    }

    protected override async void OnExit(ExitEventArgs e)
    {
        using (host)
        {
            await host.StopAsync(TimeSpan.FromSeconds(5));
        }

        base.OnExit(e);
    }
}

At line 7, we create a HostBuilder using the Host.CreateDefaultBuilder method. As we can read in the documentation, it automatically configures some defaults (that are tipically useful for every application):

If we don’t want these configuration, but instead we prefer to manually configure every setting, we can simply initialize a new HostBuilder. In any case, it is possible to invoke other methods to further configure our host:

host = Host.CreateDefaultBuilder()  // Use default settings
       //new HostBuilder()          // Initialize an empty HostBuilder
        .ConfigureAppConfiguration((context, builder) =>
        {
            // Add other configuration files...
            builder.AddJsonFile("appsettings.local.json", optional: true);
        }).ConfigureServices((context, services) =>
        {
            ConfigureServices(context.Configuration, services);
        })
        .ConfigureLogging(logging => 
        {
            // Add other loggers...
        })
        .Build();

Going back to the first App.xaml.cs file, at line 8 we call HostBuilder.ConfigureService, that is responsible to add all our services in the .NET Core IoC Container. This method in turn calls our ConfigureServices implementation (lines 15-20), in which we register all the services used by the application in the exact same way of ASP. NET Core. We’ll complete this method in a moment, but for now let’s notice that we register also the MainWindow class (line 19). This is important because, in this way, the window itself becomes part of the Dependency Injection chain. It means that, after calling this method, at line 26-27 we can get it from the ServiceProvider and then show it. But, more important, it means that we can pass to the MainWindow constructor all the dependencies it needs, as we do for ASP.NET Core Controllers.

In the OnStartup override (lines 22-30), we call the StartAsync method to start the program (line 24): at this moment, it calls IHostedService.StartAsync on each implementation of IHostedService that it finds in the DI container (we don’t have any of this in our app, so in fact this method does nothing). Finally, as said before, at line 26-27 we get the MainWindow from the ServiceProvider and then show it.

As last step, in the OnExit method (lines 32-39), we call StopAsync to gracefully stop the host.

Even if the actual services aren’t yet registered, we can run the application and see that everything works as expected.

Now it’s time to complicate the things a bit. Let’s add a file named appsettings.json to the root folder of the project. Set its Build Action property to Content and Copy to Output Directory to Copy if newer:

{
  "AppSettings": {
    "StringSetting": "Value",
    "IntegerSetting": 42,
    "BooleanSetting": true
  }
}

This file is automatically loaded and made available to the application by the CreateDefaultBuilder method we talked before. Then, we create an AppSettings.cs file to hold configuration settings. This file will map the settings that we write in appsettings.json:

public class AppSettings
{
    public string StringSetting { get; set; }
 
    public int IntegerSetting { get; set; }
 
    public bool BooleanSetting { get; set; }
}

Moreover, create also a sample service with its interface:

public interface ISampleService
{
    string GetCurrentDate();
}
 
public class SampleService : ISampleService
{
    public string GetCurrentDate() => DateTime.Now.ToLongDateString();
}

Now we must register these services in the IoC Container, as usual:

private void ConfigureServices(IConfiguration configuration,
    IServiceCollection services)
{
    services.Configure<AppSettings>
            (configuration.GetSection(nameof(AppSettings)));

    services.AddScoped<ISampleService, SampleService>();

    //...
}

As said before, the MainWindow itself is in the IoC Container. So, when we get it from the Service Provider, it will automatically be injected with all the required services. So, we just need to modify its constructor:

public partial class MainWindow : Window
{
    private readonly ISampleService sampleService;
    private readonly AppSettings settings;
 
    public MainWindow(ISampleService sampleService, 
                      IOptions<AppSettings> settings)
    {
        InitializeComponent();
 
        this.sampleService = sampleService;
        this.settings = settings.Value;
    }
 
    // ...
}

Running this code, we’ll obtain a result like the following:

The .NET Core 3.0 WPF application with dependecies injected

The .NET Core 3.0 WPF application with dependecies injected

You can download the sample app using the link below:

Update on using HostBuilder, Dependency Injection and Service Provider with .NET Core 3.0 WPF applications

Categories: .NET Core, C#, WPF
  1. Oliver Münzberg
    07/11/2019 at 22:38

    Oh, is MVVM dead on .NET Core 3.0?

    • 08/11/2019 at 10:06

      Absolutely no 🙂 In fact, MVVM fits fine on this application model. I’ll write a post about this argument soon.

      • Tim
        21/11/2019 at 23:55

        This is awesome. I had hoped that it would be possible to use the configuration and DI libraries in .NET Core 3.0 in a converted WPF application, so finding this made my day. I tried it, but it doesn’t appear to show anything. The shell shows up but nothing else gets rendered. I’ve registered Views and ViewModels but nothing seems to work. Any ideas?

      • 22/11/2019 at 18:50

        Without seeing the code, it is difficult to figure out the issue. Can you can share it?

  2. Endrit Llenga
    03/12/2019 at 11:21

    Hello, in the previous article https://marcominerva.wordpress.com/2019/03/06/using-net-core-3-0-dependency-injection-and-service-provider-with-wpf/ it was possible to configure Logging with ConfigureLogging method in the App constructor. How to do it in this version? I see that ConfigurationBuilder doesn’t have a way to do it.

  3. Endrit Llenga
    04/12/2019 at 10:14

    Thanks for your help. I managed to use this approach with navigation also. I have another question… which way to show MainWindow?

    This

    var navigationService = host.Services.GetRequiredService();
    var task = navigationService.ShowAsync();

    or this

    var mainWindow = host.Services.GetRequiredService();
    mainWindow.Show();

    At first sight they do the same job. Are they equivalent?

    • 04/12/2019 at 14:09

      As it is the Main Window of the application, you can safely use the second snippet.

      • Endrit Llenga
        04/12/2019 at 15:49

        Thank you! Great articles with the latest technologies and very quick reply from you Marco!

  4. M I N C A D E V
    05/12/2019 at 16:14

    How to use MVVM pattern

    • 06/12/2019 at 09:33

      I’ll write a post about using the MVVM Pattern as soon as possible!

    • Oliver Münzberg
      06/12/2019 at 10:29

      Until then I have a very small POC application on github you may find helpful https://github.com/SirRufo/HostedWpf

  1. 07/11/2019 at 14:52
  2. 08/11/2019 at 07:01
  3. 08/11/2019 at 14:15
  4. 07/01/2020 at 15:01
Comments are closed.