使用 Microsoft.Extensions.Hosting.WindowsServices 时无法启动 windows 服务

Cannot start windows service when using Microsoft.Extensions.Hosting.WindowsServices

我尝试按照建议 here 使用 Microsoft.Extensions.Hosting.WindowsServices 创建 Windows 服务。到目前为止一切顺利,我的后台服务 ExecuteAsync 被调用并且日志显示一切正常。作为控制台应用程序启动应用程序也很好,我可以启动它,做我需要做的任何事情,然后停止它。

但是,我随后尝试使用以下方法安装 Windows 服务:

sc create myservice binPath= "\"<path-to-the-exe-file>\" service" start= auto DisplayName= "My Service"

我得到 [SC] CreateService SUCCESS。但是当我尝试手动启动该服务时,它告诉我它没有及时响应。同样,日志很好,没有错误。事件查看器没有告诉我它可能出错的更多信息,我不知道接下来我该怎么做才能找到问题的原因。

这是我用来配置主机的大概代码:

var containerBuilder = new ContainerBuilder();
IContainer container = null; 

var hostBuilder = Host.CreateDefaultBuilder(appArgs);
hostBuilder
    .UseServiceProviderFactory(new CustomAutofacServiceProviderFactory(() => container))
    .ConfigureServices(services =>
    {
        services.AddHostedService<BackgroundWorker>();
        containerBuilder.Populate(services);
        container = containerBuilder.Build();
    })
    .UseWindowsService();

这是我用于后台服务的class。

public class BackgroundWorker : BackgroundService
{
    private readonly IAppContext appContext;
    private CancellationTokenRegistration stopRegistration;

    public BackgroundWorker(ILogger<BackgroundWorker> logger, IAppContext appContext)
    {
        this.Logger = logger;
        this.appContext = appContext;
    }

    public ILogger<BackgroundWorker> Logger { get; }

    protected override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        this.Logger.Info("Background worker started.");

        this.stopRegistration = stoppingToken.Register(() =>
        {
            this.Logger.Info("Background worker stopping...");
            this.stopRegistration.Dispose();
            this.Logger.Info("Background worker stopped.");
        });

        return Task.CompletedTask;
    }
}

经过几个小时的努力,我刚刚找到了答案。

我正在使用 Autofac 作为依赖注入容器,在 HostBuilder.ConfigureServices() 我正在构建 Autofac 容器。

在我调用 .UseWindowsService() 之后,为时已晚,因为 Autofac 已经完成了容器的构建。

所以,答案是,在构建容器之前使用.UseWindowsService(),否则对组合服务没有影响。

这是正在运行的更改代码:

var containerBuilder = new ContainerBuilder();
IContainer container = null; 

var hostBuilder = Host.CreateDefaultBuilder(appArgs);
hostBuilder
    .UseServiceProviderFactory(new CustomAutofacServiceProviderFactory(() => container))
    .ConfigureServices(services =>
    {
        services.AddHostedService<BackgroundWorker>();
    })
    .UseWindowsService() //<-- Done BEFORE building container
    .ConfigureServices(services =>
    {
        containerBuilder.Populate(services);
        container = containerBuilder.Build();
    })

理想情况下,容器不应位于其余设置的外部。

您已经在使用一个自定义服务提供商工厂,它应该包含您手动执行的操作。

Autofac 已经有 AutofacServiceProviderFactory

/// <summary>
/// A factory for creating a <see cref="ContainerBuilder"/> and an <see cref="IServiceProvider" />.
/// </summary>
public class AutofacServiceProviderFactory : IServiceProviderFactory<ContainerBuilder>
{
    private readonly Action<ContainerBuilder> _configurationAction;

    /// <summary>
    /// Initializes a new instance of the <see cref="AutofacServiceProviderFactory"/> class.
    /// </summary>
    /// <param name="configurationAction">Action on a <see cref="ContainerBuilder"/> that adds component registrations to the conatiner.</param>
    public AutofacServiceProviderFactory(Action<ContainerBuilder> configurationAction = null)
    {
        _configurationAction = configurationAction ?? (builder => { });
    }

    /// <summary>
    /// Creates a container builder from an <see cref="IServiceCollection" />.
    /// </summary>
    /// <param name="services">The collection of services.</param>
    /// <returns>A container builder that can be used to create an <see cref="IServiceProvider" />.</returns>
    public ContainerBuilder CreateBuilder(IServiceCollection services)
    {
        var builder = new ContainerBuilder();

        builder.Populate(services);

        _configurationAction(builder);

        return builder;
    }

    /// <summary>
    /// Creates an <see cref="IServiceProvider" /> from the container builder.
    /// </summary>
    /// <param name="containerBuilder">The container builder.</param>
    /// <returns>An <see cref="IServiceProvider" />.</returns>
    public IServiceProvider CreateServiceProvider(ContainerBuilder containerBuilder)
    {
        if (containerBuilder == null) throw new ArgumentNullException(nameof(containerBuilder));

        var container = containerBuilder.Build();

        return new AutofacServiceProvider(container);
    }
}

请注意容器构建器是如何由工厂和服务提供者创建和填充的IContainer

这应该允许完成原始设置,而无需在外部手动创建容器

var hostBuilder = Host.CreateDefaultBuilder(appArgs)
    .UseServiceProviderFactory(new AutofacServiceProviderFactory())
    .ConfigureServices(services => {
        services.AddHostedService<BackgroundWorker>();            
    });

引用Documentation

参考NuGet