Verwenden des generischen .NET-Hosts in einer WPF-App

Der .NET Generic Host bietet eine standardisierte Möglichkeit zum Konfigurieren und Ausführen von Anwendungen mit integrierter Unterstützung für Abhängigkeitsinjektion (Dependency Injection, DI), Konfiguration und Protokollierung. WPF-Anwendungen enthalten standardmäßig keine Host Builder-Integration, aber Sie können sie hinzufügen. In diesem Artikel wird gezeigt, wie Sie den generischen Host in einer WPF-App einrichten, um Dienste in Ihre Fenster einzufügen und die vollständige .NET-Hostinginfrastruktur zu verwenden.

Voraussetzungen

Einrichten des generischen Hosts

So integrieren Sie den generischen Host in Ihre WPF-App:

  1. Entfernen Sie StartupUri aus der XAML-Datei der Anwendung, und verbinden Sie die Ereignishandler Startup und Exit.

    <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. Erstellen Sie den Host im CodeBehind.

    Die Application_Startup Methode erstellt den Host mit CreateApplicationBuilder, registriert Dienste für die Services Eigenschaft, startet den Host und zeigt das Hauptfenster an:

    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. Beenden und löschen Sie den Host, wenn die Anwendung beendet wird, um Ressourcen zu bereinigen:

    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
    

Erstellen eines Diensts

Die Services Eigenschaft im vorherigen Abschnitt registriert Dienste mit dem DI-Container. So erstellen Sie einen benutzerdefinierten Dienst:

  1. Definieren einer Dienstschnittstelle:

    public interface IGreetingService
    {
        string GetGreeting();
    }
    
    Public Interface IGreetingService
    
        Function GetGreeting() As String
    
    End Interface
    
  2. Erstellen Sie eine Klasse, die die Schnittstelle implementiert. Die GreetingService Klasse akzeptiert IConfiguration durch Konstruktorinjektion, um Werte von appsettings.json zu lesen.

    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
    

Ausführen eines gehosteten Diensts

Der generische Host kann auch Hintergrunddienste ausführen, die am Lebenszyklus der Anwendung teilnehmen. Der Host ruft eine IHostedService Implementierung auf, wenn sie gestartet und beendet wird. So fügen Sie einen gehosteten Dienst hinzu:

  1. Erstellen Sie eine Klasse, die das IHostedService implementiert. Die folgende Klasse schreibt in die Debugausgabe, sobald der Host gestartet wird und endet.

    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. Registrieren Sie den Dienst mit der Eigenschaft Services des Builders, wie in der Methode Application_Startup im Abschnitt Generic Host einrichten gezeigt.

Der Host ruft StartAsync beim Starten und StopAsync beim Beenden auf, damit die Debugausgabe im Ausgabefenster in Visual Studio erscheint.

Einfügen von Diensten in ein Fenster

Der DI-Container injiziert Abhängigkeiten über Constructor Injection in registrierte Windows. So nutzen Sie Dienste in einem Fenster:

  1. Fügen Sie Konstruktorparameter für jeden Dienst hinzu, den das Fenster benötigt.
  2. Speichern Sie die eingefügten Dienste in privaten Feldern.
  3. Verwenden Sie die Dienste in Ereignishandlern oder anderen Methoden.

Der folgende Code zeigt, wie MainWindowILogger<MainWindow> und IGreetingService durch seinen Konstruktor akzeptiert:

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

Hinzufügen einer Konfiguration

CreateApplicationBuilder lädt appsettings.json automatisch, wenn sich die Datei im Ausgabeverzeichnis befindet. So fügen Sie Ihrem Projekt eine Konfigurationsdatei hinzu:

  1. Erstellen Sie eine appsettings.json Datei im Projektstamm mit Ihren Konfigurationswerten:

    {
      "GreetingMessage": "Hello from the .NET Generic Host!"
    }
    
  2. Legen Sie CopyToOutputDirectory auf PreserveNewest in der Projektdatei fest, damit die Datei in das Ausgabeverzeichnis kopiert wird.

    <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>