如何 运行 带有 BackgroundService 和依赖注入的 Winforms?

How to run Winforms with BackgroundService and Dependency Injection?

我正在尝试启动分配给 services.HostedServiceBackgroundService IHostBuilder.

IHostBuilder定义:

private static IHostBuilder CreateHostBuilder()
    {
        return Host.CreateDefaultBuilder()
            .ConfigureServices(services =>
            {
                services.AddHostedService<LicenseControl>();

                services.AddTransient<Database>();
                services.AddTransient<CoreLicense>();

                services.AddSingleton<FormSelector>();

                services.AddTransient<ClienteForm>();
                services.AddTransient<GastoForm>();
                services.AddTransient<IngredienteForm>();
                services.AddTransient<ProdutoForm>();
                services.AddTransient<ReceitaForm>();
                services.AddTransient<RelatorioForm>();
                services.AddTransient<VendaForm>();
                services.AddTransient<MainForm>();
                services.AddTransient<ConfiguracoesForm>();
                services.AddTransient<UsuarioForm>();

                services.AddSingleton<AuthenticationForm>();
                
                services.AddScoped<IBridgeRepository, BridgeRepository>();
                services.AddScoped<IClienteRepository, ClienteRepository>();
                services.AddScoped<IGastoRepository, GastoRepository>();
                services.AddScoped<IIBGERepository, IBGERepository>();
                services.AddScoped<IIngredienteRepository, IngredienteRepository>();
                services.AddScoped<IProdutoRepository, ProdutoRepository>();
                services.AddScoped<IReceitaRepository, ReceitaRepository>();
                services.AddScoped<IVendaRepository, VendaRepository>();
                services.AddScoped<IUsuarioRepository, UsuarioRepository>();

                services.AddLogging(
                builder =>
                {
                    builder.AddFilter("Microsoft", LogLevel.Warning)
                           .AddFilter("System", LogLevel.Warning)
                           .AddFilter("NToastNotify", LogLevel.Warning)
                           .AddConsole();
                }
                );
            });
    }

但我发现启动 HostedService 的唯一可能方法是 运行 宁此:

await CreateHostBuilder().Build().RunAsync();   

最后,问题发生了,因为在这种方法中,线程锁定在那一行并且不让我 运行 启动 Form 的公共块代码本身:

Application.Run(new MainForm());

如果我首先 运行 Form 会发生同样的情况,线程会锁定该行并且不会让我调用 HostBuilder 的 RunAsync

我还尝试在 Form class 上声明所有 HostBuilder 范围,并在其构造函数上启动 BackgroundService,但是那么我将无法 运行 ctor 上的 Async 方法。

我目前正在尝试(但还没有成功)是在没有 Application.Run 的情况下调用 Form (并且仍然不知道这样做的所有副作用)所以我可以 运行 接下来 HostBuilder

我的整个程序 class现在:

static class Program
{
    [STAThread]
    private static async Task Main()
    {
        Application.SetHighDpiMode(HighDpiMode.SystemAware);
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);            

        var host = CreateHostBuilder().Build();

        host.Services.GetRequiredService<AuthenticationForm>().Show();

        await host.RunAsync();
    }
    private static IHostBuilder CreateHostBuilder()
    {
        return Host.CreateDefaultBuilder()
            .ConfigureServices(services =>
            {
                services.AddHostedService<LicenseControl>();

                services.AddTransient<Database>();
                services.AddTransient<CoreLicense>();

                services.AddSingleton<FormSelector>();

                services.AddTransient<ClienteForm>();
                services.AddTransient<GastoForm>();
                services.AddTransient<IngredienteForm>();
                services.AddTransient<ProdutoForm>();
                services.AddTransient<ReceitaForm>();
                services.AddTransient<RelatorioForm>();
                services.AddTransient<VendaForm>();
                services.AddTransient<MainForm>();
                services.AddTransient<ConfiguracoesForm>();
                services.AddTransient<UsuarioForm>();

                services.AddSingleton<AuthenticationForm>();
                
                services.AddScoped<IBridgeRepository, BridgeRepository>();
                services.AddScoped<IClienteRepository, ClienteRepository>();
                services.AddScoped<IGastoRepository, GastoRepository>();
                services.AddScoped<IIBGERepository, IBGERepository>();
                services.AddScoped<IIngredienteRepository, IngredienteRepository>();
                services.AddScoped<IProdutoRepository, ProdutoRepository>();
                services.AddScoped<IReceitaRepository, ReceitaRepository>();
                services.AddScoped<IVendaRepository, VendaRepository>();
                services.AddScoped<IUsuarioRepository, UsuarioRepository>();

                services.AddLogging(
                builder =>
                {
                    builder.AddFilter("Microsoft", LogLevel.Warning)
                           .AddFilter("System", LogLevel.Warning)
                           .AddFilter("NToastNotify", LogLevel.Warning)
                           .AddConsole();
                }
                );
            });
    }
}

考虑到 .NET 5 并忽略 BackgroundWorker 组件,有什么解决方法?

Nkosi 的解决方案,评论:

Make another hosted service to invoke Application.Run that way when you run the host, the UI aspect and background worker hosted services will be started

class StartProgram : BackgroundService
{
    private readonly IServiceProvider _services;
    public StartProgram(IServiceProvider services)
    {
        this._services = services;
    }
    protected override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        Application.SetHighDpiMode(HighDpiMode.SystemAware);
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run((AuthenticationForm)_services.GetService(typeof(AuthenticationForm)));

        return Task.CompletedTask;
    }
}

和 Program.cs:

static class Program
{
    [STAThread]
    private static async Task Main()
    {
        await CreateHostBuilder().Build().RunAsync();
    }
    private static IHostBuilder CreateHostBuilder()
    {
        return Host.CreateDefaultBuilder()
            .ConfigureServices(services =>
            {
                services.AddHostedService<LicenseControl>();
                services.AddHostedService<StartProgram>();

                #region ETC...
            });
    }
}