Usare l'host generico .NET in un'app Windows Forms

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 app Windows Form non includono l'integrazione dell'host generico per impostazione predefinita, ma è possibile aggiungerla. Questo articolo illustra come configurare l'host generico in un'app Windows Form per inserire i servizi nei moduli.

Prerequisiti

Configurare l'host generico

La configurazione è leggermente diversa tra C# e Visual Basic. In C# configurare l'host direttamente in Program.cs. In Visual Basic usare gli eventi di avvio e arresto di Application Framework in ApplicationEvents.vb.

Configurare l'host insieme a Program.cs e ApplicationConfiguration.Initialize().

  1. Chiamare ApplicationConfiguration.Initialize() per configurare le impostazioni predefinite di WinForms, inclusi gli stili di visualizzazione, la modalità DPI elevata e i tipi di carattere predefiniti.
  2. Compilare l'host con CreateApplicationBuilder e registrare i servizi.
  3. Avviare l'host e risolvere il modulo principale dall'inserimento delle dipendenze.
  4. Passare il modulo a Run.
  5. Arrestare ed eliminare l'host dopo la chiusura del modulo.

Il codice seguente mostra il codice completo Program.cs:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace HostBuilderApp;

static class Program
{
    [STAThread]
    static void Main()
    {
        ApplicationConfiguration.Initialize();

        var builder = Host.CreateApplicationBuilder();

        builder.Services.AddHostedService<SampleLifecycleService>();
        builder.Services.AddTransient<Form1>();
        builder.Services.AddSingleton<IGreetingService, GreetingService>();

        IHost host = builder.Build();

        host.Start();

        Form1 mainForm = host.Services.GetRequiredService<Form1>();
        Application.Run(mainForm);

        host.StopAsync().GetAwaiter().GetResult();
        host.Dispose();
    }
}

Registrare e utilizzare i servizi

Dopo aver configurato l'host, registrare i servizi personalizzati e inserirli nei moduli. Per creare e registrare un servizio:

  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 inserisce IConfiguration per leggere il messaggio di saluto da appsettings.json:

    public class GreetingService : IGreetingService
    {
        private readonly IConfiguration _configuration;
    
        public GreetingService(IConfiguration configuration)
        {
            _configuration = configuration;
        }
    
        public string GetGreeting()
        {
            return _configuration.GetValue<string>("GreetingMessage")
                ?? "Hello, World!";
        }
    }
    
    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 message Is Nothing Then
                Return "Hello, World!"
            End If
    
            Return message
        End Function
    
    End Class
    
  3. Registrare l'interfaccia e l'implementazione nella proprietà del Services builder, come illustrato nella sezione Configurare l'host generico.

Eseguire un servizio ospitato

L'host generico può anche eseguire servizi in background che partecipano al ciclo di vita dell'applicazione. Implementare IHostedService per ricevere i callback all'avvio e all'arresto dell'host. 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 builder, come mostrato nella sezione Configurare l'host generico.

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

Consumare i servizi in un formulario

Poiché Form1 viene risolto dal contenitore DI, l'inserimento del costruttore funziona direttamente:

  1. Aggiungere i parametri del costruttore per ogni servizio necessario al modulo.
  2. Archiviare i servizi iniettati in campi privati.
  3. Usare i servizi nei gestori di eventi o in altri metodi.
public partial class Form1 : Form
{
    private readonly ILogger<Form1> _logger;
    private readonly IGreetingService _greetingService;

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

        _logger = logger;
        _greetingService = greetingService;
    }

    private void ButtonGreet_Click(object sender, EventArgs e)
    {
        string greeting = _greetingService.GetGreeting();
        lblGreeting.Text = greeting;
        _logger.LogInformation("Greeting displayed: {Greeting}", greeting);
    }
}
Public Class Form1

    Private _logger As ILogger(Of Form1)
    Private _greetingService As IGreetingService

    Sub New(ILogger As ILogger(Of Form1), greetingService As IGreetingService)
        InitializeComponent()
        _logger = ILogger
        _greetingService = greetingService
    End Sub

    Private Sub ButtonGreet_Click(sender As Object, e As EventArgs) Handles btnGreet.Click
        Dim greeting As String = _greetingService.GetGreeting()
        lblGreeting.Text = greeting
        _logger.LogInformation("Greeting displayed: {Greeting}", greeting)
    End Sub

End Class

Aggiungere la configurazione

CreateApplicationBuilder carica appsettings.json automaticamente dalla directory di output. Per aggiungere un file di configurazione:

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

    {
      "GreetingMessage": "Hello from the Generic Host!"
    }
    
  2. Impostare CopyToOutputDirectory su PreserveNewest nel file di progetto in modo che appsettings.json 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>
        <UseWindowsForms>true</UseWindowsForms>
      </PropertyGroup>
    
      <ItemGroup>
        <None Update="appsettings.json">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
        </None>
      </ItemGroup>
    
      <ItemGroup>
        <PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.*" />
      </ItemGroup>
    
    </Project>