How to use RabbitMQ with a C# producer and a C# consumer

In this blog post, we will explore how RabbitMQ can be used to create a system with a C# producer and a C# consumer, and how the consumer can save messages into a PostgreSQL database using Entity Framework as the ORM. To make this even easier to set up, we will use Docker to run a local instance of RabbitMQ.

Suppose you’re working for a weather monitoring company that provides real-time weather data analysis for businesses. To do this, you need access to a high volume data source like the National Oceanic and Atmospheric Administration’s (NOAA) Global Forecast System (GFS). The GFS provides access to global weather data, which can be a massive amount of data to process in real-time. In this situation, a messaging service like RabbitMQ can help you manage this data effectively.

The producer will be written in C# and will connect to the GFS API to retrieve weather data. It will then send this data to RabbitMQ as messages. The consumer will also be written in C# and will receive these messages from RabbitMQ. It will then use Entity Framework as the ORM to save the messages into a PostgreSQL database.

To get started, we need to set up a local instance of RabbitMQ using Docker. First, we need to install Docker on our machine. Once Docker is installed, we can run the following command in the terminal to pull the latest version of the RabbitMQ image:

docker pull rabbitmq

Once the image is downloaded, we can run the following command to start a RabbitMQ container:

docker run -d --hostname my-rabbit --name some-rabbit -p 5672:5672 -p 15672:15672 rabbitmq:latest

This command starts a RabbitMQ container with the name some-rabbit and sets the hostname to my-rabbit. We also map the default RabbitMQ ports (5672 and 15672) to the corresponding ports on our machine so that we can access the RabbitMQ web interface.

Now that we have RabbitMQ up and running, we can proceed with the C# producer and consumer code. First, we need to establish a connection to the GFS API using a library like RestSharp. We can then create a RabbitMQ connection and channel using the RabbitMQ .NET client library. Once we have the channel, we can publish messages to RabbitMQ using the channel’s BasicPublish method. Here’s some sample code to give you an idea of what this might look like:

// Connect to the GFS APIvar client = new RestClient("https://api.weather.gov"); var request = new RestRequest("gridpoints/MLB/25,69/forecast", Method.GET); var response = client.Execute(request); var data = response.Content; // Create a RabbitMQ connection and channelvar factory = new ConnectionFactory() { HostName = "localhost" }; using (var connection = factory.CreateConnection()) 	    using (var channel = connection.CreateModel()) { 		        // Declare the exchange and queue    		channel.ExchangeDeclare(exchange: "weather_data", type: ExchangeType.Fanout); 		        channel.QueueDeclare(queue: "weather_data_queue", durable: false, exclusive: false, autoDelete: false, arguments: null);         // Publish the message to RabbitMQ            var message = Encoding.UTF8.GetBytes(data); channel.BasicPublish(exchange: "weather_data", routingKey: "", basicProperties: null, body: message);         Console.WriteLine(" [x] Sent {0}", data);     }

The consumer can also be written in C# using the RabbitMQ .NET client library. It can receive messages from RabbitMQ using the channel’s BasicConsume method. Once it receives a message,it can use Entity Framework to save the message to the PostgreSQL database. Here’s an example of what the consumer code might look like:

// Create a RabbitMQ connection and channelvar factory = new ConnectionFactory() { HostName = "localhost" }; using (var connection = factory.CreateConnection())     using (var channel = connection.CreateModel()) {         // Declare the queue            channel.QueueDeclare(queue: "weather_data_queue", durable: false, exclusive: false, autoDelete: false, arguments: null);         // Create a consumer            var consumer = new EventingBasicConsumer(channel);         consumer.Received += (model, ea) => {             var body = ea.Body.ToArray();             var message = Encoding.UTF8.GetString(body);             Console.WriteLine(" [x] Received {0}", message);             // Save the message to the database using Entity Framework                    using (var dbContext = new WeatherDbContext()) {                 var weatherData = new WeatherData { Data = message }; dbContext.WeatherData.Add(weatherData);                 dbContext.SaveChanges();     	} 	}; // Start consuming messages from RabbitMQ    channel.BasicConsume(queue: "weather_data_queue", autoAck: true, consumer: consumer); Console.WriteLine(" Press [enter] to exit."); Console.ReadLine(); }

In this example, we used Docker to run a local instance of RabbitMQ on our machine. We then used C# to create a producer that connected to the GFS API and sent messages to RabbitMQ, and a consumer that received these messages from RabbitMQ and used Entity Framework to save them to a PostgreSQL database.

This system can be used to manage real-time data and process it effectively, making it a valuable tool for monitoring and analysis in a high transactional environment.

How to calculate age in C#

Today I had a requirement to calculate someone’s age based on that person’s birthday. It seems like a pretty simple function to write, but I decided to Google around a little bit anyway to see if what other people were doing to calculate age based on birthday. Turns out, a few people have ideas about this. The first Google result is a Stack Overflow post titled How do I calculate someone’s age based on a DateTime type birthday? that is full of ways to calculate the age. The post is over 13 years old but is plum full with so many different methods of calculating an age based off of a birthday.

Below are two ways that I found to be popular in the post as well as a pretty cool library that calculates age in C# with a single method call.

//TLDR; There is a great .Net 5+ library called AgeCalculator that calculates age based off of two DateTime values. Unfortunately, as far as I can tell the AgeCalculator library is only available for .Net 5+. So, in if you’re on an older version of .Net you should take a look at this Stack Overflow thread or the second example below for ways to calculate age in C#.

Calculating age based on year alone

My initial thought was to find age by subtracting the birthday year from today’s year. However, I quickly realized that doesn’t work so well, unless the two dates are the exact same day. Since that is not the case, this method does not take into account the offset in days and months as well as other factors(leap years, etc) so this is not best way to make this calculation.

using System;					
public class Program{	
  public static void Main()	{		
    var birthday = new DateTime(1980,12,11);		
    var age = DateTime.Now.Year - birthday.Year;		
    Console.WriteLine(age);		//Output: 42	
  }
}

Calculating age based on number of days

The method that seems to work well is by James Current from this Stack Overflow comment which takes into consideration the days/months between now and the birthday. This seems to be the preferred method to return the accurate age based off the birthday. If you cannot use the AgeCalculator package for .Net 5+ this could be a valid option.

using System;
public class Program {
  public static void Main() {
    var birthday = new DateTime(1980, 12, 11);
    int age = (int)((DateTime.Now - birthday).TotalDays / 365.242199);
    Console.WriteLine(age);
    //Output: 41	
  }
}

Calculating age using AgeCalculator library

For .Net 5+ code bases you can use this AgeCalculator library. As you can see, you have access immediately to the age based off a new object’s constructor.

using System;
public class Program {
  public static void Main() {
    var birthday = new DateTime(1980, 12, 11);
    var age = new Age(birthday, DateTime.Today);
    Console.WriteLine(age.Years); //Output: 41	
  }
}

Other ways to use this library

Based on the documentation of the library there are a whole lot of ways you can use this to calculate age. Here are just a few ways that I took from the README file on the AgeCalculator library’s Github page. It seems like a pretty extensible library to use for dates, I’m excited to use this in more projects.

/* There are three ways to calculate the age between two dates */
using AgeCalculator;
using AgeCalculator.Extensions;
public void PrintMyAge() {
    // Date of birth or from date.    
    var dob = DateTime.Parse("10/03/2015"); 
    // #1. Using the Age class constructor.    
    var age = new Age(dob, DateTime.Today); // as of 09/19/2021    
    Console.WriteLine($"Age: {age.Years} years, {age.Months} months, {age.Days} days, {age.Time}"); 
    // #2. Using DateTime extension.    
    age = dob.CalculateAge(DateTime.Today); // as of 09/19/2021    
    Console.WriteLine($"Age: {age.Years} years, {age.Months} months, {age.Days} days, {age.Time}");        
    // #3. Using the Age type's static function.    
    age = Age.Calculate(dob, DateTime.Today); // as of 09/19/2021    
    Console.WriteLine($"Age: {age.Years} years, {age.Months} months, {age.Days} days, {age.Time}");}
    // Output:
    // Age: 5 years, 11 months, 16 days, 00:00:00

Why I Write Unit Tests

In computer programming, unit testing is a software testing method by which individual units of source code, sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures, are tested to determine whether they are fit for use — wikipedia

Unit tests, who needs em? We all do. There are a lot of resources online that will tell you why unit testing is important or why writing unit tests before building implementations is a good idea. But I’m here to tell you how unit tests can save you a massive production headache.

A while back, a coworker pushed code to production that consumed and parsed large .csv files uploaded by users. The code looped through the file then parsed and mapped each record to the associated record in the model. The model contained a few bool fields that were populated by executing the C# Boolean.TryParse method against the text field to convert the string from the file to bool. If the TryParse method returned false then the value in the csv file must have been falsy, and vice versa for true. The record was applied to the model and saved to the database.

As it turned out, our code didn’t work as expected which caused a major problem. For bool fields users would upload either a 1 or 0. 1 being true and 0 being false. Because the source was a csv file all values were imported as strings. Therefore, before applying the values to our model, we must convert the string representation to the appropriate type within the model.

Here’s an example of the code, the Parse method below takes a string argument named parsable, does a Boolean.TryParse against parsable and returns the newly converted value.

		public static Main(){	    var trueFalse = Parse("1");	}		public bool Parse(string parsable)	{		 bool flag;		 bool result = Boolean.TryParse(parsable, out flag);         return flag;	}

From looking at the method and without knowing how C# Boolean.TryParse converts the string argument “1” to bool you’d think that this method returns true, not false. But in reality the above method returns false. Even though 1 generally equals true in most contexts, if passed in as a string the result is false, not true.

If we would have had proper unit tests set up we would have written a unit test that looks something like this:

[TestMethod]public void Parse_BooleanTryParseString__ReturnsFalse(){    string stringToParse = "1";    bool expected = true;    bool actual = Parse(stringToParse);    Assert.AreEqual(expected, actual);}

The above unit test would have failed and we’d have found the problem, fixed it and been on our way. Unfortunately for us, we didn’t have a unit test for the above Parse method and QA didn’t catch the issue during their testing. The code eventually made its way to production, users start uploading files against the new code and yadda, yadda, yadda…we end up with a lot false values that should be true.

I've made a huge mistake

The good news is we were able to recover the lost data. But the recovery process took a few days and a lot of manual work which set us back on our timeline, not to mention created a garbage truck full of unwanted stress while we scrambled to recover the data and explain to our stakeholders that we’d made a huge mistake.

The takeaway my team and I had from this experience is how valuable a unit test can be. Unit tests are not just a key part of the testing process, but they’ve proven to be invaluable in making sure the code you write is actually doing what you think it’s doing.