De .NET Generic Host gebruiken in een WPF-app

De .NET Generic Host biedt een gestandaardiseerde manier om toepassingen te configureren en uit te voeren met ingebouwde ondersteuning voor afhankelijkheidsinjectie (DI), configuratie en logboekregistratie. WPF-toepassingen bevatten standaard geen Host Builder-integratie, maar u kunt deze toevoegen. In dit artikel wordt beschreven hoe u de algemene host instelt in een WPF-app om services in uw vensters te injecteren en de volledige .NET-hostinginfrastructuur te gebruiken.

Vereiste voorwaarden

De algemene host instellen

De algemene host integreren met uw WPF-app:

  1. Verwijder StartupUri uit het XAML-bestand van de toepassing en verbind de Startup-handlers en Exit-handlers.

    <Application x:Class="HostBuilderApp.App"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 Startup="Application_Startup"
                 Exit="Application_Exit">
        <Application.Resources>
        </Application.Resources>
    </Application>
    
  2. Bouw de host in de code-behind.

    De Application_Startup methode maakt de host met CreateApplicationBuilder, registreert services op de Services eigenschap, start de host en toont het hoofdvenster:

    private async void Application_Startup(object sender, StartupEventArgs e)
    {
        var builder = Host.CreateApplicationBuilder();
    
        builder.Services.AddHostedService<SampleLifecycleService>();
        builder.Services.AddSingleton<IGreetingService, GreetingService>();
        builder.Services.AddSingleton<MainWindow>();
    
        _host = builder.Build();
    
        await _host.StartAsync();
    
        MainWindow mainWindow = _host.Services.GetRequiredService<MainWindow>();
        mainWindow.Show();
    }
    
    Private Async Sub Application_Startup(sender As Object, e As StartupEventArgs)
        Dim builder = Host.CreateApplicationBuilder()
    
        builder.Services.AddHostedService(Of SampleLifecycleService)()
        builder.Services.AddSingleton(Of IGreetingService, GreetingService)()
        builder.Services.AddSingleton(Of MainWindow)()
    
        _host = builder.Build()
    
        Await _host.StartAsync()
    
        Dim mainWindow As MainWindow = _host.Services.GetRequiredService(Of MainWindow)()
        mainWindow.Show()
    End Sub
    
  3. Stop en verwijder de host wanneer de toepassing wordt afgesloten om resources op te schonen:

    private async void Application_Exit(object sender, ExitEventArgs e)
    {
        using (_host)
        {
            await _host.StopAsync();
        }
    }
    
    Private Async Sub Application_Exit(sender As Object, e As ExitEventArgs)
        Using _host
            Await _host.StopAsync()
        End Using
    End Sub
    

Een service maken

De Services eigenschap in de vorige sectie registreert services bij de DI-container. Een aangepaste service maken:

  1. Een service-interface definiëren:

    public interface IGreetingService
    {
        string GetGreeting();
    }
    
    Public Interface IGreetingService
    
        Function GetGreeting() As String
    
    End Interface
    
  2. Maak een klasse waarmee de interface wordt geïmplementeerd. De GreetingService klasse accepteert IConfiguration via constructorinjectie om waarden te lezen van appsettings.json:

    public class GreetingService(IConfiguration configuration) : IGreetingService
    {
        public string GetGreeting() =>
            configuration.GetValue<string>("GreetingMessage")
            ?? "Hello from the Generic Host!";
    }
    
    Public Class GreetingService
        Implements IGreetingService
    
        Private ReadOnly _configuration As IConfiguration
    
        Public Sub New(configuration As IConfiguration)
            _configuration = configuration
        End Sub
    
        Public Function GetGreeting() As String Implements IGreetingService.GetGreeting
            Dim message As String = _configuration.GetValue(Of String)("GreetingMessage")
    
            If String.IsNullOrEmpty(message) Then
                Return "Hello from the Generic Host!"
            End If
    
            Return message
        End Function
    
    End Class
    

Een gehoste service uitvoeren

De algemene host kan ook achtergrondservices uitvoeren die deelnemen aan de levenscyclus van de toepassing. De host roept een IHostedService implementatie aan wanneer deze wordt gestart en gestopt. Een gehoste service toevoegen:

  1. Maak een klasse die IHostedService implementeert. De volgende klasse schrijft naar de foutopsporingsuitvoer wanneer de host wordt gestart en stopt:

    public class SampleLifecycleService : IHostedService
    {
        public Task StartAsync(CancellationToken cancellationToken)
        {
            System.Diagnostics.Debug.WriteLine("SampleLifecycleService: Started.");
            return Task.CompletedTask;
        }
    
        public Task StopAsync(CancellationToken cancellationToken)
        {
            System.Diagnostics.Debug.WriteLine("SampleLifecycleService: Stopped.");
            return Task.CompletedTask;
        }
    }
    
    Public Class SampleLifecycleService
        Implements IHostedService
    
        Public Function StartAsync(cancellationToken As CancellationToken) As Task Implements IHostedService.StartAsync
            System.Diagnostics.Debug.WriteLine("SampleLifecycleService: Started.")
            Return Task.CompletedTask
        End Function
    
        Public Function StopAsync(cancellationToken As CancellationToken) As Task Implements IHostedService.StopAsync
            System.Diagnostics.Debug.WriteLine("SampleLifecycleService: Stopped.")
            Return Task.CompletedTask
        End Function
    
    End Class
    
  2. Registreer de service met de eigenschap Services van de opbouwer AddHostedService, zoals getoond in de methode Application_Startup in de sectie Algemene host instellen.

De host roept StartAsync aan bij het starten en StopAsync bij het stoppen, zodat de foutopsporingsuitvoer wordt weergegeven in het venster Uitvoer in Visual Studio.

Services in een venster injecteren

De DI-container injecteert afhankelijkheden in geregistreerde vensters via constructorinjectie. Services gebruiken in een venster:

  1. Voeg constructorparameters toe voor elke service die het venster nodig heeft.
  2. Sla de geïnjecteerde services op in privévelden.
  3. Gebruik de diensten in gebeurtenishandlers of andere methoden.

De volgende code toont MainWindow dat ILogger<MainWindow> en IGreetingService accepteert via zijn constructor.

public partial class MainWindow : Window
{
    private readonly ILogger<MainWindow> _logger;
    private readonly IGreetingService _greetingService;

    public MainWindow(ILogger<MainWindow> logger, IGreetingService greetingService)
    {
        InitializeComponent();

        _logger = logger;
        _greetingService = greetingService;
    }

    private void OnGetGreetingClick(object sender, RoutedEventArgs e)
    {
        _logger.LogInformation("Get Greeting button clicked.");
        GreetingText.Text = _greetingService.GetGreeting();
    }
}
Class MainWindow

    Private ReadOnly _logger As ILogger(Of MainWindow)
    Private ReadOnly _greetingService As IGreetingService

    Public Sub New(logger As ILogger(Of MainWindow), greetingService As IGreetingService)
        InitializeComponent()

        _logger = logger
        _greetingService = greetingService
    End Sub

    Private Sub OnGetGreetingClick(sender As Object, e As RoutedEventArgs)
        _logger.LogInformation("Get Greeting button clicked.")
        GreetingText.Text = _greetingService.GetGreeting()
    End Sub

End Class

Configuratie toevoegen

CreateApplicationBuilder wordt automatisch geladen appsettings.json wanneer het bestand zich in de uitvoermap bevindt. Ga als volgende te werk om een configuratiebestand toe te voegen aan uw project:

  1. Maak een appsettings.json bestand in de hoofdmap van het project met uw configuratiewaarden:

    {
      "GreetingMessage": "Hello from the .NET Generic Host!"
    }
    
  2. Ingesteld CopyToOutputDirectory op PreserveNewest in het projectbestand, zodat het bestand naar de uitvoermap wordt gekopieerd:

    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFramework>net10.0-windows</TargetFramework>
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
        <UseWPF>true</UseWPF>
      </PropertyGroup>
    
      <ItemGroup>
        <None Update="appsettings.json">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
        </None>
      </ItemGroup>
    
      <ItemGroup>
        <PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.*" />
      </ItemGroup>
    
    </Project>