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

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?

  1. 07/11/2019 at 14:52
  2. 08/11/2019 at 07:01
  3. 08/11/2019 at 14:15

Leave a comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: