.net core 3.1 HostBuilder 没有 RunAsServiceAsync 方法(IHostBuilder 不包含 RunAsServiceAsync 的定义)

.net core 3.1 HostBuilder not having RunAsServiceAsync method (IHostBuilder does not contain definition for RunAsServiceAsync)

我有 .net core 3.1 控制台应用程序,我想 运行 它作为 windows 服务,我的 program.cs 看起来像

public class Program
    {
        public static async Task Main(string[] args)
        {
            var isService = !(Debugger.IsAttached || args.Contains("--console"));

            var builder = CreateHostBuilder(args);

            if (isService)
            {
                await builder.RunAsServiceAsync();
            }
            else
            {
                await builder.RunConsoleAsync();
            }
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
            .UseWindowsService()
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddHostedService<Worker1>();
                    services.AddHostedService<Worker2>();
                });
    }

.csproj 是

<Project Sdk="Microsoft.NET.Sdk.Worker">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <UserSecretsId>dotnet-MyWorkerService-16487890-DF99-45C2-8DC4-5475A21D6B75</UserSecretsId>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.16" />
    <PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="3.1.16" />
  </ItemGroup>
</Project>

但是对于 RunAsServiceAsync() 会出现错误,例如“IHostBuilder 不包含 RunAsServiceAsync 的定义”

任何人都可以指出我在哪里/缺少什么吗?

RunAsServiceAsync 似乎是 IHostBuilder 上的第 3 方扩展。

它似乎不是 .NET Core 原生的内置函数。

我在 GitHub here 上找到了一个旧的实现,您可以自己实现

public static class ServiceBaseLifetimeHostExtensions
{
    public static IHostBuilder UseServiceBaseLifetime(this IHostBuilder hostBuilder)
    {
        return hostBuilder.ConfigureServices((hostContext, services) => services.AddSingleton<IHostLifetime, ServiceBaseLifetime>());
    }

    public static Task RunAsServiceAsync(this IHostBuilder hostBuilder, CancellationToken cancellationToken = default)
    {
        return hostBuilder.UseServiceBaseLifetime().Build().RunAsync(cancellationToken);
    }
}

public class ServiceBaseLifetime : ServiceBase, IHostLifetime
{
    private TaskCompletionSource<object> _delayStart = new TaskCompletionSource<object>();

    public ServiceBaseLifetime(IApplicationLifetime applicationLifetime)
    {
        ApplicationLifetime = applicationLifetime ?? throw new ArgumentNullException(nameof(applicationLifetime));
    }

    private IApplicationLifetime ApplicationLifetime { get; }

    public Task WaitForStartAsync(CancellationToken cancellationToken)
    {
        cancellationToken.Register(() => _delayStart.TrySetCanceled());
        ApplicationLifetime.ApplicationStopping.Register(Stop);

        new Thread(Run).Start(); // Otherwise this would block and prevent IHost.StartAsync from finishing.
        return _delayStart.Task;
    }

    private void Run()
    {
        try
        {
            Run(this); // This blocks until the service is stopped.
            _delayStart.TrySetException(new InvalidOperationException("Stopped without starting"));
        }
        catch (Exception ex)
        {
            _delayStart.TrySetException(ex);
        }
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        Stop();
        return Task.CompletedTask;
    }

    // Called by base.Run when the service is ready to start.
    protected override void OnStart(string[] args)
    {
        _delayStart.TrySetResult(null);
        base.OnStart(args);
    }

    // Called by base.Stop. This may be called multiple times by service Stop, ApplicationStopping, and StopAsync.
    // That's OK because StopApplication uses a CancellationTokenSource and prevents any recursion.
    protected override void OnStop()
    {
        ApplicationLifetime.StopApplication();
        base.OnStop();
    }
}

但是当在构建器上调用 UseWindowsService 时,似乎现在内置了这个基于服务的功能。

因此,在这种情况下,您需要相应地重构代码以获得所需的行为

public class Program {
    public static async Task Main(string[] args) {
        var isService = !(Debugger.IsAttached || args.Contains("--console"));

        var builder = CreateHostBuilder(args);

        if (isService) {
            await builder.RunAsServiceAsync();
        } else {
            await builder.RunConsoleAsync();
        }
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)            
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHostedService<Worker1>();
                services.AddHostedService<Worker2>();
            });
}

public static class ServiceBaseLifetimeHostExtensions {
    public static Task RunAsServiceAsync(this IHostBuilder hostBuilder, CancellationToken cancellationToken = default) {
        return hostBuilder.UseWindowsService().Build().RunAsync(cancellationToken);
    }
}