Hospedagem de Worker Services do .NET como Serviços do Windows

Gerenciamento de Serviços via Linha de Comando

Para hospedar uma aplicação Worker do .NET como um Serviço do Windows nativo, é fundamental compreender o utilitário sc.exe (Service Control). Esta ferramenta nativa permite criar, configurar, iniciar e remover serviços diretamente pelo prompt de comendo.

Scripts de Instalação e Remoção

Abaixo estão exemplos de comandos para automatizar o ciclo de vida do serviço. Certifique-se de executar o terminal com privilégios administrativos.

:: install_service.bat
@echo off
set SVC_NAME=DataSyncWorker
set SVC_PATH="C:\Applications\DataSync\bin\Release\net8.0\DataSyncWorker.exe"
set SVC_DESC="Serviço de sincronização de dados em segundo plano"

sc.exe create %SVC_NAME% binPath= %SVC_PATH% start= auto
sc.exe description %SVC_NAME% %SVC_DESC%
sc.exe start %SVC_NAME%
pause

:: uninstall_service.bat
@echo off
set SVC_NAME=DataSyncWorker

sc.exe stop %SVC_NAME%
sc.exe delete %SVC_NAME%
pause

Comandos Individuais do sc.exe

Caso prefira executar as operações manualmente, utilize a seguinte sintaxe:

  • Criar: sc.exe create NomeDoServico binPath= "C:\caminho\para\executavel.exe"
  • Iniciar: sc.exe start NomeDoServico
  • Parar: sc.exe stop NomeDoServico
  • Remover: sc.exe delete NomeDoServico
  • Alterar Descrição: sc.exe description NomeDoServico "Nova descrição detalhada"
  • Modificar Tipo de Inicialização: sc.exe config NomeDoServico start= demand (Opções: auto, demand, disabled)

Implementação no Código .NET

O processo de adaptação de um projeto Worker para rodar como um serviço Windows exige a adição de pacotes específicos e pequenas alterações na configuração do host.

1. Referência de Pacotes

Adicione as dependências necessárias ao seu arquivo .csproj:

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

2. Configuração do Host (Program.cs)

Configure o construtor do host para utilizar o provedor de serviços do Windows. Isso permite que o aplicativo responda corretamente aos sinais de inicio e parada do SCM (Service Control Manager).

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace WindowsServiceHostingDemo
{
    public class Program
    {
        public static void Main(string[] arguments)
        {
            var host = Host.CreateDefaultBuilder(arguments)
                .UseWindowsService(options =>
                {
                    options.ServiceName = "DataSyncWorker";
                })
                .ConfigureServices((context, collection) =>
                {
                    collection.AddHostedService<TaskExecutor>();
                })
                .Build();

            host.Run();
        }
    }
}

3. Lógica do Worker (TaskExecutor.cs)

A classe de execução deve herdar de BackgroundService. Neste exemplo, a lógica foi refactorada para utilizar operações de arquivo assíncronas e garantir a criação do diretório de logs de forma segura.

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace WindowsServiceHostingDemo
{
    public class TaskExecutor : BackgroundService
    {
        private readonly ILogger<TaskExecutor> _logger;
        private readonly string _traceFilePath;

        public TaskExecutor(ILogger<TaskExecutor> logger)
        {
            _logger = logger;
            _traceFilePath = Path.Combine(AppContext.BaseDirectory, "Traces", "activity.log");
            
            var directory = Path.GetDirectoryName(_traceFilePath);
            if (!Directory.Exists(directory))
            {
                Directory.CreateDirectory(directory!);
            }
        }

        public override async Task StartAsync(CancellationToken cancellationToken)
        {
            await RegisterEventAsync("Service Initialized");
            await base.StartAsync(cancellationToken);
        }

        public override async Task StopAsync(CancellationToken cancellationToken)
        {
            await RegisterEventAsync("Service Terminated");
            await base.StopAsync(cancellationToken);
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                _logger.LogInformation("Background task executing at: {Time}", DateTime.UtcNow);
                await RegisterEventAsync($"Execution cycle completed at {DateTime.UtcNow}");
                await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
            }
        }

        private async Task RegisterEventAsync(string message)
        {
            var logEntry = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {message}{Environment.NewLine}";
            await File.AppendAllTextAsync(_traceFilePath, logEntry);
        }
    }
}

Considerações de Implantação

Ao publicar a aplicação, utilize o comando dotnet publish -c Release -r win-x64 para gerar os binários autocontidos. Ao registrar múltiplos serviços no mesmo servidor, mantenha um padrão de nomenclatura rigoroso para os nomes e descrições, facilitando a identificação e manutenção via Gerenciador de Serviços do Windows.

Tags: dotnet CSharp windows-services backgroundservice worker-service

Publicado em 7-2 03:53