Exercise - Send and receive messages by using a queue
You've chosen to use a Service Bus queue to handle messages about individual sales between the mobile app, which your sales personnel use, and the web service hosted in Azure, which stores details about each sale in an Azure SQL Database instance.
In the previous exercise, you implemented the necessary objects in your Azure subscription. Now, you want to write code that sends messages to that queue and retrieves messages.
In this unit, you build two console applications: one application places messages into a Service Bus queue, and one application retrieves messages from a Service Bus queue. The applications are part of a single .NET Core solution.
Clone and open the starter application
Note
For simplicity, the following tasks instruct you to hard-code the connection string in the Program.cs file of both console applications. In a production application, you should use role-based access control authentication supported by Azure Service Bus.
Launch GitBash on your computer.
Run the following command to clone the Git project solution:
cd ~ git clone https://github.com/MicrosoftDocs/mslearn-connect-services-together.git
Launch VS Code on your computer.
Select File -> Open Folder ..., and then select the folder: C:\Program Files\Git\learn-pr\learn-pr\azure\implement-message-workflows-with-service-bus (assuming that C:\Program Files\Git is the Git folder). You should see final and start subfolders. You work with code in the start folder to write code to send and receive messages. The final folder contains the code that's complete.
Write code to send a message to a queue
In the Code Explorer window to the left, expand privatemessagesender.
Open Program.cs, and locate the following line of code:
const string ServiceBusConnectionString = "";
Paste the connection string between the quotation marks.
If you used a name different from
salesmessages
for the queue name, update the value forQueueName
property in the code:const string QueueName = "salesmessages";
To complete the component that sends messages about sales, you must add an
await
operator to suspend evaluation of the async method until the asynchronous operation completes. Find theSendSalesMessageAsync()
method. Within that method, locate the following line of code:// Create a Service Bus client here
Replace that line of code with the following code:
// By leveraging "await using", the DisposeAsync method will be called automatically once the client variable goes out of scope. // In more realistic scenarios, you would want to store off a class reference to the client (rather than a local variable) so that it can be used throughout your program. await using var client = new ServiceBusClient(ServiceBusConnectionString);
Within the
SendSalesMessageAsync()
method, find the following line of code:// Create a sender here
Replace that comment with the following code:
await using ServiceBusSender sender = client.CreateSender(QueueName);
Within the
try...catch
block, find the following line of code:// Create and send a message here
Replace that line of code with the following lines of code:
string messageBody = $"$10,000 order for bicycle parts from retailer Adventure Works."; var message = new ServiceBusMessage(messageBody);
Insert the following code on a new line directly below what you just added to display the message in the console:
Console.WriteLine($"Sending message: {messageBody}");
Insert the following code on the next line:
await sender.SendMessageAsync(message);
To dispose sender and client objects, near the end of the file, find the following comment:
// Close the connection to the sender here
Replace that line with the following code:
finally { // Calling DisposeAsync on client types is required to ensure that network // resources and other unmanaged objects are properly cleaned up. await sender.DisposeAsync(); await client.DisposeAsync(); }
Check that your final code for Program.cs resembles the following example:
using System; using System.Text; using System.Threading.Tasks; using Azure.Messaging.ServiceBus; namespace privatemessagesender { class Program { const string ServiceBusConnectionString = "Endpoint=sb://example.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=xxxxxx"; const string QueueName = "salesmessages"; static void Main(string[] args) { Console.WriteLine("Sending a message to the Sales Messages queue..."); SendSalesMessageAsync().GetAwaiter().GetResult(); Console.WriteLine("Message was sent successfully."); } static async Task SendSalesMessageAsync() { await using var client = new ServiceBusClient(ServiceBusConnectionString); await using ServiceBusSender sender = client.CreateSender(QueueName); try { string messageBody = $"$10,000 order for bicycle parts from retailer Adventure Works."; var message = new ServiceBusMessage(messageBody); Console.WriteLine($"Sending message: {messageBody}"); await sender.SendMessageAsync(message); } catch (Exception exception) { Console.WriteLine($"{DateTime.Now} :: Exception: {exception.Message}"); } finally { // Calling DisposeAsync on client types is required to ensure that network // resources and other unmanaged objects are properly cleaned up. await sender.DisposeAsync(); await client.DisposeAsync(); } } } }
To save your changes, select File -> Save on the menu or enter Ctrl+S.
Send a message to the queue
In the Code Explorer, right-click privatemessagesender, and then select Open in Integrated Terminal.
In the terminal pane, at the command prompt, confirm that you are in the privatemessagesender folder, and then enter the following command:
dotnet build
.Now, run the application by running the following command:
dotnet run
. As the program runs, messages are printed to the console indicating that the app is sending a message:Sending a message to the Sales Messages queue... Sending message: $10,000 order for bicycle parts from retailer Adventure Works. Message was sent successfully. ```
When the app is finished, run the following command, replacing <namespace-name> with the name of your Service Bus namespace. This command returns the number of messages that are in the queue.
az servicebus queue show \ --resource-group "<rgn>[sandbox resource group name]</rgn>" \ --name salesmessages \ --query messageCount \ --namespace-name <namespace-name>
Write code to receive messages from the queue
In the Code Explorer page, expand privatemessagereceiver.
Open Program.cs, and find the following line of code:
const string ServiceBusConnectionString = "";
Between the quotation marks, paste the connection string that you saved earlier.
Find the
ReceiveSalesMessageAsync()
method. Within that method, locate the following line of code:// Create a Service Bus client that will authenticate using a connection string
Replace that line with the following code:
var client = new ServiceBusClient(ServiceBusConnectionString);
To configure message handling options, find the following line of code:
// Create the options to use for configuring the processor
Replace that line with the following lines of code:
var processorOptions = new ServiceBusProcessorOptions { MaxConcurrentCalls = 1, AutoCompleteMessages = false };
To create a processor, find the following line of code:
// Create a processor that we can use to process the messages
Replace that line with the following code:
await using ServiceBusProcessor processor = client.CreateProcessor(QueueName, processorOptions);
To configure the handlers, find the following line of code:
// Configure the message and error handler to use
Replace that line with the following code:
processor.ProcessMessageAsync += MessageHandler; processor.ProcessErrorAsync += ErrorHandler;
To start processing, find the following line of code:
// Start processing
Replace that line with the following code:
await processor.StartProcessingAsync();
To close the connection to Service Bus, find the following line of code:
// Close the processor here
Replace that line with the following code:
await processor.CloseAsync();
Review code in the
MessageHandler
method:// handle received messages static async Task MessageHandler(ProcessMessageEventArgs args) { // extract the message string body = args.Message.Body.ToString(); // print the message Console.WriteLine($"Received: {body}"); // complete the message so that message is deleted from the queue. await args.CompleteMessageAsync(args.Message); }
Review code in the
ErrorHandler
method:// handle any errors when receiving messages static Task ErrorHandler(ProcessErrorEventArgs args) { // print the exception message Console.WriteLine(args.Exception.ToString()); return Task.CompletedTask; }
Check that your final code for privatemessagereceiver/Program.cs resembles the following example:
using System; using System.Text; using System.Threading.Tasks; using Azure.Messaging.ServiceBus; namespace privatemessagereceiver { class Program { const string ServiceBusConnectionString = "Endpoint=sb://<examplenamespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; const string QueueName = "salesmessages"; static void Main(string[] args) { ReceiveSalesMessageAsync().GetAwaiter().GetResult(); } static async Task ReceiveSalesMessageAsync() { Console.WriteLine("======================================================"); Console.WriteLine("Press ENTER key to exit after receiving all the messages."); Console.WriteLine("======================================================"); var client = new ServiceBusClient(ServiceBusConnectionString); var processorOptions = new ServiceBusProcessorOptions { MaxConcurrentCalls = 1, AutoCompleteMessages = false }; await using ServiceBusProcessor processor = client.CreateProcessor(QueueName, processorOptions); processor.ProcessMessageAsync += MessageHandler; processor.ProcessErrorAsync += ErrorHandler; await processor.StartProcessingAsync(); Console.Read(); await processor.CloseAsync(); } // handle received messages static async Task MessageHandler(ProcessMessageEventArgs args) { string body = args.Message.Body.ToString(); Console.WriteLine($"Received: {body}"); // complete the message. messages is deleted from the queue. await args.CompleteMessageAsync(args.Message); } // handle any errors when receiving messages static Task ErrorHandler(ProcessErrorEventArgs args) { Console.WriteLine(args.Exception.ToString()); return Task.CompletedTask; } } }
To save your changes, select File -> Save on the menu, or enter Ctrl+S.
Receive a message from the queue
In the Code Explorer, right-click privatemessagereceiver, and then select Open in Integrated Terminal.
In the terminal pane, at the command prompt, confirm that you are in the privatemessagereceiver folder, and then enter the following command:
dotnet build
.Now, run the application by running the following command:
dotnet run
.====================================================== Press ENTER key to exit after receiving all the messages. ====================================================== Received: $10,000 order for bicycle parts from retailer Adventure Works.
When you see that the messages have been received in the Cloud Shell, ENTER to stop the app.
Check the message count
Run the following code to confirm that all the messages have been removed from the queue, remembering to replace <namespace-name> with your Service Bus namespace.
az servicebus queue show \
--resource-group "<rgn>[sandbox resource group name]</rgn>" \
--name salesmessages \
--query messageCount \
--namespace-name <namespace-name>
The output is 0
if all the messages have been removed.
You've written code that sends a message about individual sales to a Service Bus queue. In the salesforce distributed application, you should write this code in the mobile app that sales personnel use on devices.
You've also written code that receives a message from the Service Bus queue. In the salesforce distributed application, you should write this code in the web service that runs in Azure and processes received messages.