Arquitectura del Generic Host
La incorporación del Generic Host en el ecosistema .NET ha estandarizado la creación de procesos ejecutados en segundo plano. Esta herramienta nativa sustituye la necesidad de librerías de terceros al ofrecer un ciclo de vida administrado por defecto, inyección de dependencias integrada y capacidad de depuración directa mediante herramientas estándar (ejecución con F5). La arquitectura resultante garantiza consistencia operativa tanto en antornos de desarrollo como en producción.
Configuración Inicial y Estructura Base
Para generar la solución, se requiere un SDK igual o superior a la versión 3.0. Al seleccionar la plantilla Worker Service, el entorno de desarrollo crea automáticamente dos módulos centrales: el punto de entrada que configura el contenedor de servicios y la entidad que contiene la tarea cíclica.
A continuación se presenta una implementación refactorizada que mantiene la funcionalidad original pero optimiza la lectura y separación de responsabilidades:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Threading;
using System.Threading.Tasks;
namespace PlataformaServiciosFondo
{
public class Program
{
public static void Main(string[] parametros)
{
ConstruirYLanzar(parmetros).Build().Run();
}
private static IHostBuilder ConstruirYLanzar(string[] argumentos) =>
Host.CreateDefaultBuilder(argumentos)
.ConfigureServices((context, colector) =>
{
colector.AddHostedService<tareaoperativa>();
// Registro adicional de servicios mediante inyección compatible
});
}
public class TareaOperativa : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken detencion)
{
while (!detencion.IsCancellationRequested)
{
// Ejecución de lógica de negocio programada
await RealizarProcesamiento(detencion);
await Task.Delay(TimeSpan.FromSeconds(15), detencion);
}
}
private static async Task RealizarProcesamiento(CancellationToken token)
{
// Simulación de operación asíncrona
await Task.CompletedTask;
}
}
}</tareaoperativa>
Adaptación Multiplataofrma
El modelo anterior es completamente agnóstico al sistema operativo, pero la interacción con el gestor de servicios requiere paquetes específicos. Para Windows se instala Microsoft.Extensions.Hosting.WindowsServices y para distribuciones Linux con init systemd se añade Microsoft.Extensions.Hosting.Systemd. Una misma compilación puede manejar ambos comportamientos utilizando reflexión de plataforma:
private static IHostBuilder DefinirContexto(string[] args)
{
var baseConstructor = Host.CreateDefaultBuilder(args);
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? baseConstructor.UseWindowsService()
.ConfigureServices((_, s) => s.AddHostedService<tareaoperativa>())
: baseConstructor.UseSystemd()
.ConfigureServices((_, s) => s.AddHostedService<tareaoperativa>());
}</tareaoperativa></tareaoperativa>
Despliegue en Entornos Windows
La instalación en el registro de controladores exige privilegios elevados. Utilizando CMD o PowerShell como administrador, se registra el servicio especificando la ruta física del ejecutable:
sc.exe create NombreServicio binPath="C:\Ruta\Absoluta\bin\net6.0\Ejecutable.exe"
Un mensaje de confirmación indica éxito. Para ponerlo en marcha:
sc.exe start NombreServicio
Nota crítica: en PowerShell, la extensión .exe es obligatoria después de sc para evitar colisiones con el cmdlet Start-Service.
Despliegue en Entornos Linux
El procedimiento sigue estándares POSIX y systemd. Previo a la copia de los artefactos compilados, se recomienda crear un usuario restringido para aislar los permisos:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin operadordotnet
Se genera un descriptor de unidad en /etc/systemd/system/nombre-servicio.service:
[Unit]
Description=Daemon de Procesamiento .NET
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/bin/dotnet /var/lib/app/Servicio.dll
WorkingDirectory=/var/lib/app
User=operadordotnet
Group=operadordotnet
Restart=on-failure
RestartSec=8s
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
Una vez persistido el archivo, se actualiza el demonio, se activa el enlace simbólico de arranque y se levanta la instancia:
sudo systemctl daemon-reload
sudo systemctl enable nombre-servicio.service
sudo systemctl start nombre-servicio.service
sudo systemctl status nombre-servicio.service
Gestión de Ciclo de Vida y Limitaciones Operativas
La clase base expone hooks como StartAsync y StopAsync, permitiendo interceptar señales SIGTERM/SIGINT para finalizar operaciones pendientes o cerrar conexiones de forma limpia. Sin embargo, este enfoque no integra directamente opciones avanzadas del SCM de Windows, como pausas manuales prolongadas, políticas de error granulares o dependencias explícitas de otros servicios. Estas restricciones imponen un diseño más enfocado en la simplicidad y la portabilidad cruzada.
Diferencias frente a Alternativas Tradicionales
- Ventajas técnicas: Adopción nativa de Cross-Platform sin emuladores ni capas de abstracción pesadas. Sincronización total con el pipeline de inyección de dependencias utilizado en APIs HTTP, reduciendo la curva de adaptación para desarrolladores Full-Stack.
- Restricciones actuales: Ausencia de comandos auxiliares integrados (instalación/desinstalación vía CLI) que obligen al uso de utilidades del SO. Compatibilidad estricta con arquitecturas .NET Core 3.0+ y .NET 5+, excluyendo entornos Mono o frameworks heredados.