Crear un servicio con C# y .NET6 o posteriores

Publicado el 10.09.2023 a las 22:14

Crear un servicio con C# y .NET6 o posteriores

  1. Crear un programa de tipo Worker Service

    • Añadiendo dependencia necesaria

    • Creando nuestro programa

    • Editando la clase Worker

    • Editando la clase Program

    • Probando nuestro servidor HTTP

  2. Compilando nuestra aplicación para que se comporte como un servicio

  3. Crear el servicio

    • Comprobar que el servicio se ha registrado

    • Iniciando y parando el servicio

  4. Eliminar el servicio

  5. Repositorio

Crear un servicio con C# y .NET6 o posteriores

Es inevitable tener que ir migrando nuestro software por diferentes motivos, los nuevos Sistemas Operativos ya no lo soportan, falta de soporte, brechas de seguridad...


Esta semana he tenido que migrar un software que se ejecuta como un servicio de Windows y lo he hecho a .NET Core (antes se le llamaba .NET Core).


¿Por qué no lo he migrado a .NET Framework y así me ahorro más trabajo?

Porque probablemente el servidor donde va a correr este servicio se migre a la nube y para ahorrar costes se contrate un servidor con Linux 🐧

Al programar el servicio sobre .NET (.NET Core), mi servicio funcionará también en Linux 😎


La versión de .NET que he escogido ha sido la versión 6.

Sé que ya está disponible .NET 7, pero la compatibilidad será mayor para .NET 6, según Microsoft.

Compatibilidad a largo plazo de .NET6

Crear un programa de tipo Worker Service

Lo primero que haremos será abrir nuestro Visual Studio y crear un nuevo proyecto de tipo Worker Service.

Compatibilidad a largo plazo de .NET6

Elegimos un nombre, en mi caso EjemploServicioDotNET.


Elegimos el Framework en el cuál desarrollaremos. Como ya te he dicho será .NET6.


Como resultado, Visual Studio nos generará un proyecto con la siguiente estructura:

Estructura de un proyecto tipo Woker Service en .NET6

Añadiendo dependencia necesaria

Para interoperar con servicios nativos de Windows a partir de implementaciones de .NET IHostedService, deberás instalar el paquete NuGet Microsoft.Extensions.Hosting.WindowsServices .

Instalando Microsoft.Extensions.Hosting.WindowsServices

Después de añadir dicha dependencia, tu archivo de proyecto (el mío es EjemploServicioDotNET.csproj) debería tener el siguiente contenido:

Contenido del archivo del proyecto después de añadir Microsoft.Extensions.Hosting.WindowsServices

Creando nuestro programa

En este ejemplo voy a crear un servidor HTTP que responderá a una petición GET.


Nada muy sofisticado 😅


Creo la clase MyHTTPServer y añado el siguiente código:

public MyHTTPServer() {
    HttpListener _listener = new();
    Thread _listenerThread;

    _listenerThread = new(StartListener);
    const string url = "http://localhost:8888/";

    _listener.Prefixes.Add(url);
    _listener.Start();
    _listenerThread.Start();

    Console.WriteLine("Servidor HTTP iniciado en " + url);

    void StartListener()
    {
        while (_listener.IsListening)
        {
            var context = _listener.GetContext();
            var request = context.Request;                       {                

            var response = context.Response;
            string miRespuesta = "Hola desde el servidor HTTP de @fjmduran";
                                                          
            if (request.HttpMethod == "GET")
            {
                miRespuesta += "\nHa hecho una petición GET";
            }

            byte[] responseBytes = System.Text.Encoding.UTF8.GetBytes(miRespuesta);
            response.ContentLength64 = responseBytes.Length;
            response.OutputStream.Write(responseBytes, 0, responseBytes.Length);
            response.OutputStream.Close();
        }
    }
}

Editando la clase Worker

La clase Worker será la encargada de ejecutar la acción de nuestro servicio.


La parte interesante es la función ExecuteAsync, es aquí donde programaremos lo que queremos que haga nuestro servicio.


En el await Task.Delay(1000, stoppingToken) le indicaremos en milisegundos el tiempo que debe esperar entre ejecuciones.


Si no incluimos esta parte, el método se volverá a ejecutar instantáneamente cuando finalice.


El tiempo se indica en milisegundos.


Deja el código de tu Worker.cs así:

public class Worker : BackgroundService
{     
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        MyHTTPServer myHTTPServer = new();
        while (!stoppingToken.IsCancellationRequested)
        {                
            await Task.Delay(1000, stoppingToken);
        }
    }
}
        

Editando la clase Program

Edita tu clase Program y déjala así:

using EjemploServicioDotNET;

using Microsoft.Extensions.Logging.Configuration;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddWindowsService(options =>
{
    options.ServiceName = "Test Servicio con .NET";
});

builder.Services.AddSingleton<MyHTTPServer>();
builder.Services.AddHostedService<Worker>();

// See: https://github.com/dotnet/runtime/issues/47303
builder.Logging.AddConfiguration(
    builder.Configuration.GetSection("Logging"));

IHost host = builder.Build();
host.Run();
        

El método de extensión AddWindowsService configura la aplicación para que funcione como un servicio de Windows.


El nombre del servicio se establece como "Test Servicio con .NET".


El servicio alojado se registra para la inyección de dependencias.


Si quieres más información sobre el registro de servicios, consulta Inyección de dependencias en .NET.

Probando nuestro servidor HTTP

Si ejecutamos nuestra aplicación desde Visual Studio debería de correr perfectamente:

Servidor HTTP corriendo en Visual Studio

Si hacemos una petición GET a nuestro servidor, por ejemplo usando Postman deberías ver:

Petición GET desde Postman a nuestro servidor HTTP

Compilando nuestra aplicación para que se comporte como un servicio

Para crear la aplicación .NET Worker Service como un servicio de Windows, se recomienda publicar la aplicación como un único archivo ejecutable.


Es menos propenso a errores tener un ejecutable autocontenido, ya que no hay archivos dependientes en el sistema de archivos.


No obstante, puedes elegir una modalidad de publicación diferente, lo cual es perfectamente aceptable, siempre y cuando crees un archivo *.exe que pueda ser apuntado por el Administrador de Control de Servicios de Windows.


Vamos al turrón, dale a publicar en carpeta y te generará todos los ficheros necesarios para que tu servicio corra en Windows.

Publicar servicio de Windows con Visual StudioOpciones para publicar servicio de Windows

Crear el servicio

Para crear el servicio abriremos una consolo con privilegios de administrador y ejecutaremos:

sc create "Test Servicio con .NET" binpath="D:\fjmduran\programacion\net\EjemploServicioDotNET\bin\Release\net6.0\publish\EjemploServicioDotNET.exe"
Servicio registrado satisfactoriamente

Comprobar que el servicio se ha registrado

Para comprobar que el servicio se ha registrado abriremos los servicios de Windows como administrador y buscaremos el nuestro, que le asignamos el nombre de Test Servicio con .NET

Buscando el servicio Test Servicio con .NET en los servicios de Windows

Iniciando y parando el servicio

No puede ser más fácil, con un botón de play y stop


En la siguiente imagen puedes ver que el servicio ya está corriendo porque está el botón de play deshabilitado y porque en la columna de Estado puedes leer En ejecución.

Servicio Test Servicio con .NET en ejecución

Si vuelves a hacer una petición GET al puerto 8888 verás como te responde satisfactoriamente.

Eliminar el servicio

sc delete "Test Servicio con .NET" binpath="D:\fjmduran\programacion\net\EjemploServicioDotNET\bin\Release\net6.0\publish\EjemploServicioDotNET.exe"

Repositorio de código de un servicio para Windows con .NET 6

Encontrarás el repositorio con todos los archivos de código en este enlace de mi GitHub 🔥


Hasta luego 🖖