Abstracting Azure Service Bus And Azure Queues To Send Messages

Building modern applications requires developers to constantly adopt newer technologies over time. Over the last few years, multiple queuing technologies have been introduced, with various protocols and SDKs, which can make it difficult for developers to quickly adopt new technologies over time.

In this article, I will demonstrate how to write a simple queuing interface that can be used to send messages with the Azure Service Bus and Azure Queues using C# designed to abstract the main source code from the actual underlying queuing technology so that the code is easier to write and easier to upgrade over time when new technologies are introduced. You can leverage this code to extend it with other queuing technologies as well (such as MQ Series and RabbitMQ), and add features to the underlying logic to support more advanced capabilities of individual platforms.

The Concept

To keep things simple, let’s build an interface that will allow an application to send a single message as a string (a JSON document for example). The sample application will simply send a message as a string using a library that we will call QueueLib. This library will have two classes (AZBus and AZQueue) that can send a message in either the Azure Bus or an Azure Queue. This will be done through an interface so that the client code doesn’t need to know much about the internals of the queuing platform being used.

The Concept

Note that the ability to choose which class to use can be as simple as letting the client code make the decision, implementing some additional logic based on configuration settings, or even implementing an Inversion of Control mechanism. In this article, I am choosing to add simple logic that will determine which class to instantiate based on the configuration settings of the application.

About the QueueLib Library

Let’s first create the QueueLib project using C# as a .NET library. This project contains an Interface that abstracts access to the underlying objects and contains a single method signature: Send, and two properties (ConnectionString and QueueName). Note that in this example, we are not allowing message-level options to simplify the code; you could extend this example by allowing options such as a message TimeToLive parameter.

using System;

namespace QueueLib
{
    public interface IQueueService
    {
        void Send(string payload);
        string ConnectionString { get; set; }
        string QueueName { get; set; }
    }
}

Next, we will implement the AZQueue class that implements the IQueueService interface. This class will require the use of the Microsoft.Azure.Storage.Queue NuGet package. The class exposes two properties (ConnectionString and QueueName), along with the Send method.

using System;
using Microsoft.Azure.Storage;
using Microsoft.Azure.Storage.Queue;

namespace QueueLib
{
    public class AZQueue : IQueueService
    {
        public string ConnectionString { get; set; }
        public string QueueName { get; set; }
        
        public void Send(string payload)
        {
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(ConnectionString);
            CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();
            CloudQueue queue = queueClient.GetQueueReference(QueueName);
            CloudQueueMessage message = new CloudQueueMessage(payload);
            queue.AddMessage(message);
        }
    }
}

Finally, let’s implement the AZBus class that also implements the IQueueService interface. This class will require the use of Microsoft.Azure.ServiceBus NuGet package.

using Microsoft.Azure.ServiceBus;
using System.Text;

namespace QueueLib
{
    public class AZBus : IQueueService
    {
        public string ConnectionString { get; set; }
        public string QueueName { get; set; }
        private static IQueueClient queueClient;

        public void Send(string payload)
        {
            queueClient = new QueueClient(ConnectionString, QueueName);
            var message = new Message(Encoding.UTF8.GetBytes(payload));
            queueClient.SendAsync(message).Wait();
        }
    }
}

Now that both classes and the interface have been created, let’s see the client application code.

Building the Client Console Application

The client application will be using the IQueueService interface to send its messages; a method called GetQueue() will have the logic necessary to figure out which queue to return (either AZBus or AZQueue). The key here is that the logic to select the correct queue object is abstracted from the main code; in this case, the logic simply looks at the ConnectionString value in the configuration file to make that determination. Since every service will have a different format for its connection string it can be as simple as inspecting its content to return the correct object. There are more robust ways to implement this logic, but for a simple case such as this one, it’s all we need. The app settings of the client code look like this.

<appSettings>
  <add key="connectionString" value="yourconnectionstring"/>
  <add key="queueName" value="yourqueuename"/>
</appSettings>

Once the object has been created, it’s simply a matter of calling the Send() method.

using System;
using QueueLib;

namespace ConsoleAppAZQueueDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            var service = GetQueue();
            service.Send("test message");

            Console.Read();
        }

        static IQueueService GetQueue()
        {
            // Central logic that returns the queue to use
            string connectionString = System.Configuration.ConfigurationManager.AppSettings["connectionString"];
            string queueName = System.Configuration.ConfigurationManager.AppSettings["queueName"];

            // Detect which kind of queue we need to create, based on the connection string
            IQueueService service = null;
            if (connectionString.ToLower().Contains("core.windows.net"))
                service = new AZQueue();
            else if (connectionString.ToLower().Contains("sb://"))
                service = new AZBus();

            service.ConnectionString = connectionString;
            service.QueueName = queueName;

            return service;
        }
    }
}

Testing with the Azure Queue

If the connection string contains core.windows.net, we know it’s an Azure Storage Queue; in the screenshot below the message sent can be seen in the Azure Portal directly.

Azure Queue

Testing with the Service Bus Queue

If the connection string contains sb://, we know it’s an Azure Service Bus; the screenshot below shows the message sent to the Azure Bus Queue.

Service Bus Queue

Conclusion

In this simple application, we can see the benefits of abstracting the queue service in a library that will do the hard work for us. Changing the queue becomes as simple as changing the configuration settings of the application.

There are many opportunities to improve this simple code, such as the ability to receive messages, implementing queue-specific options, and a more robust strategy for choosing the queueing technology to implement just to name a few.