Usare l'host generico .NET in un'app WPF

L'host generico .NET offre un modo standardizzato per configurare ed eseguire applicazioni con supporto predefinito per l'inserimento delle dipendenze (DI), la configurazione e la registrazione. Le applicazioni WPF non includono l'integrazione di Host Builder per impostazione predefinita, ma è possibile aggiungerla. Questo articolo illustra come configurare l'host generico in un'app WPF per inserire i servizi nelle finestre e usare l'infrastruttura di hosting .NET completa.

Prerequisiti

Configurare l'host generico

Per integrare l'host generico con l'app WPF:

  1. Rimuovere StartupUri dal file XAML dell'applicazione e collegare i Startup gestori eventi e 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. Implementare l'host nel codice sottostante.

    Il Application_Startup metodo crea l'host con CreateApplicationBuilder, registra i servizi nella Services proprietà , avvia l'host e mostra la finestra principale:

    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. Fermare e liberarsi dell'host alla chiusura dell'applicazione per pulire le risorse utilizzate.

    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
    

Creare un servizio

La Services proprietà nella sezione precedente registra i servizi con il contenitore di inserimento delle dipendenze. Per creare un servizio personalizzato:

  1. Definire un'interfaccia del servizio:

    public interface IGreetingService
    {
        string GetGreeting();
    }
    
    Public Interface IGreetingService
    
        Function GetGreeting() As String
    
    End Interface
    
  2. Creare una classe che implementa l'interfaccia . La GreetingService classe accetta IConfiguration tramite l'inserimento del costruttore per leggere i valori da 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
    

Eseguire un servizio ospitato

L'host generico può anche eseguire servizi in background che partecipano al ciclo di vita dell'applicazione. L'host chiama un'implementazione IHostedService all'avvio e all'arresto. Per aggiungere un servizio ospitato:

  1. Creare una classe che implementi IHostedService. La seguente classe scrive sull'output di debug quando l'host si avvia e si arresta.

    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. Registrare il servizio con AddHostedService nella proprietà del Services del builder, come illustrato nel metodo Application_Startup nella sezione Configurare l'host generico.

L'host chiama StartAsync all'avvio e StopAsync all'arresto, in modo che l'output di debug venga visualizzato nella finestra Output in Visual Studio.

Inserire i servizi in una finestra

Il contenitore DI fornisce le dipendenze alle finestre registrate mediante l'iniezione tramite costruttore. Per utilizzare i servizi in una finestra:

  1. Aggiungere i parametri del costruttore per ogni servizio necessario alla finestra.
  2. Archiviare i servizi iniettati in campi privati.
  3. Usare i servizi nei gestori di eventi o in altri metodi.

Il codice seguente mostra MainWindow che accetta ILogger<MainWindow> e IGreetingService attraverso il suo costruttore.

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

Aggiungere la configurazione

CreateApplicationBuilder viene caricato appsettings.json automaticamente quando il file si trova nella directory di output. Per aggiungere un file di configurazione al progetto:

  1. Creare un appsettings.json file nella radice del progetto con i valori di configurazione:

    {
      "GreetingMessage": "Hello from the .NET Generic Host!"
    }
    
  2. Impostare CopyToOutputDirectory su PreserveNewest nel file di progetto in modo che il file venga copiato nella directory di output:

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