C# – Worker Services

By | 09/02/2022

In this post, we will see how to create a Worker Service in .net core to read messages in a RabbitMQ queue.
But first of all, what is a Worker Service?
From Microsoft web site:
“Background service processing usually doesn’t involve a user interface (UI), but UIs can be built around them. In the early days with .NET Framework, Windows developers could create Windows Services for these reasons. Now with .NET, you can use the BackgroundService — which is an implementation of IHostedService, or implement your own.
With .NET, you’re no longer restricted to Windows. You can develop cross-platform background services. Hosted services are logging, configuration, and dependency injection (DI) ready. They’re a part of the extensions suite of libraries, meaning they’re fundamental to all .NET workloads that work with the generic host.

We start running a docker images of RabbitMQ and then, we will enter in the dashboard to create a queue called “TestWorkService”:

docker run -d --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3.8-management



Now, we open Visual Studio and we create a Worker Service project called WorkerService:

Then, we create a class called ReadMessage where we will put the logic for reading messages in the Queue:
[IREADMESSAGE.CS]

namespace WorkerService.Service
{
    public interface IReadMessage
    {
        void Read();
    }
}



[READMESSAGE.CS]

using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;

namespace WorkerService.Service
{
    public class ReadMessage : IReadMessage
    {
        public void Read()
        {
            // Definition of Connection 
            // Obviously in a real project we mustn't put here the user and password...   
            var _rabbitMQServer = new ConnectionFactory() { HostName = "localhost", Password = "guest", UserName = "guest" };

            using var connection = _rabbitMQServer.CreateConnection();

            using var channel = connection.CreateModel();

            StartReading(channel, "TestWorkService");
        }

        private void StartReading(IModel channel, string queueName)
        {
            // connect to the queue
            channel.QueueDeclare(queueName,
                durable: true,
                exclusive: false,
                autoDelete: false,
                arguments: null);

            // Consumer definition
            var consumer = new EventingBasicConsumer(channel);

            // Definition of event when the Consumer gets a message
            consumer.Received += (sender, e) => {
                ManageMessage(e);
            };

            // Start pushing messages to our consumer
            channel.BasicConsume(queueName, true, consumer);

            Console.WriteLine("Consumer is running");
            Console.ReadLine();
        }

        private void ManageMessage(BasicDeliverEventArgs e)
        {
            var body = e.Body.ToArray();
            var message = Encoding.UTF8.GetString(body);
            Console.WriteLine(message);

            // We append the message in a file .txt
            using StreamWriter file = new("MessagesRead.txt", append: true);
            file.WriteLine(message);
        }
    }
}



Now, we modify the file Worker.cs in order to call the ReadMessage class:
[WORKER.CS]

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
using WorkerService.Service;

namespace WorkerService
{
    public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;
        private readonly IReadMessage _readMessage;

        public Worker(IReadMessage readMessage, ILogger<Worker> logger)
        {
            _logger = logger;
            _readMessage = readMessage;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                // Run the Read method
                await Task.Run(() => _readMessage.Read());
            }
        }
    }
}



Finally, we modify the file Program.cs to define the Dependency Injection for IReadMessage interface:
[PROGRAM.CS]

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using WorkerService.Service;

namespace WorkerService
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddSingleton<IReadMessage, ReadMessage>();
                    services.AddHostedService<Worker>();
                });
    }
}



We have done and now, if we run the application and we send three messages, this will be the result:

Using the command more, we can read the file MessageRead.txt:

If we check in RabbitMQ, we will see no messages in the queue:




WORKER SERVICE – CODE
https://github.com/ZoneOfDevelopment/WorkerService



Category: C# Tags:

Leave a Reply

Your email address will not be published.