Не удается запустить службу Windows при использовании Microsoft.Extensions.Hosting.WindowsServices - PullRequest
1 голос
/ 07 ноября 2019

Я пытался следовать рекомендациям здесь для создания службы Windows с использованием Microsoft.Extensions.Hosting.WindowsServices. Пока все хорошо, все работает нормально, мой фоновый сервис ExecuteAsync вызывается, и журнал говорит, что все в порядке. Запуск приложения в виде консольного приложения также работает нормально, я могу запустить его, сделать все, что мне нужно, а затем остановить его.

Однако затем я пытаюсь установить службу Windows, используя:

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

Я получаю [SC] CreateService SUCCESS. Но когда я пытаюсь запустить службу вручную, она говорит мне, что она не ответила своевременно. Опять же, журнал в порядке, без ошибок. Средство просмотра событий больше ничего не говорит мне о том, что может пойти не так, и я понятия не имею, что я могу сделать дальше, чтобы найти причину проблемы.

Вот примерный код, который я использовал для настройкиhost:

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();

И этот класс я использую для фоновой службы.

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

Ответы [ 2 ]

2 голосов
/ 07 ноября 2019

В идеале контейнер не должен быть внешним по отношению к остальной части установки.

Вы уже используете фабрику нестандартного поставщика услуг, которая должна включать в себя то, что вы делаете вручную.

У 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>();            
    });

Ссылка Документация

Ссылка NuGet

2 голосов
/ 07 ноября 2019

Я нашел ответ только сейчас, после нескольких часов борьбы.

Я использую 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();
    })
...