Uso del host genérico de .NET en una aplicación de Windows Forms

El host genérico de .NET proporciona una manera estandarizada de configurar y ejecutar aplicaciones con compatibilidad integrada con inserción de dependencias (DI), configuración y registro. Las aplicaciones de Windows Forms no incluyen la integración de host genérico de forma predeterminada, pero puede agregarla. En este artículo se muestra cómo configurar el host genérico en una aplicación de Windows Forms para insertar servicios en los formularios.

Prerrequisitos

Configuración del host genérico

La configuración difiere ligeramente entre C# y Visual Basic. En C#, configure el host directamente en Program.cs. En Visual Basic, utiliza los eventos de inicio y apagado del Application Framework en ApplicationEvents.vb.

Configure el host junto Program.cs con ApplicationConfiguration.Initialize():

  1. Llame ApplicationConfiguration.Initialize() para configurar los valores predeterminados de WinForms, incluidos los estilos visuales, el modo de alta DPI y las fuentes por defecto.
  2. Compile el host con CreateApplicationBuilder y registre los servicios.
  3. Inicie el host y resuelva el formulario principal desde la inserción de dependencias.
  4. Pase el formulario a Run.
  5. Detenga el proceso y elimine el host después de cerrar el formulario.

En el siguiente código se muestra el 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();
    }
}

Registro y consumo de servicios

Con el host configurado, registre servicios personalizados e inyecte en los formularios. Para crear y registrar un servicio:

  1. Definir una interfaz de servicio:

    public interface IGreetingService
    {
        string GetGreeting();
    }
    
    Public Interface IGreetingService
        Function GetGreeting() As String
    End Interface
    
  2. Cree una clase que implemente la interfaz . La GreetingService clase inserta IConfiguration para leer el mensaje de saludo de 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. Registre la interfaz y la implementación en la propiedad Services del generador, como se muestra en la sección Configurar el host genérico.

Ejecución de un servicio hospedado

El host genérico también puede ejecutar servicios en segundo plano que participan en el ciclo de vida de la aplicación. Implemente IHostedService para recibir callbacks cuando se inicie y detenga el host. Para agregar un servicio hospedado:

  1. Cree una clase que implemente IHostedService. La siguiente clase escribe en la salida de depuración cuando el host se inicia y detiene:

    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. Registre el servicio con AddHostedService en la propiedad del Services constructor, como se muestra en la sección Configurar el Host Genérico.

El host llama StartAsync al arrancar y StopAsync al cerrar, para que la salida de depuración aparezca en la ventana Salida de Visual Studio.

Utilización de servicios a través de un formulario

Dado que Form1 se resuelve desde el contenedor de inserción de dependencias, la inserción de constructores funciona directamente:

  1. Agregue parámetros de constructor para cada servicio que necesite el formulario.
  2. Almacene los servicios inyectados en campos privados.
  3. Use los servicios en controladores de eventos u otros métodos.
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

Agregar configuración

CreateApplicationBuilder carga appsettings.json automáticamente desde el directorio de salida. Para agregar un archivo de configuración:

  1. Cree un appsettings.json archivo en la raíz del proyecto con los valores de configuración:

    {
      "GreetingMessage": "Hello from the Generic Host!"
    }
    
  2. CopyToOutputDirectory Establezca PreserveNewest en el archivo del proyecto de modo que appsettings.json sea copiado en el directorio de salida:

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