Azure.Messaging.ServiceBus (.NET)
Enterprise messaging SDK for reliable message delivery with queues, topics, subscriptions, and sessions.
Installation
bash
dotnet add package Azure.Messaging.ServiceBusdotnet add package Azure.IdentityCurrent Version: v7.20.1 (stable)
Environment Variables
bash
AZURE_SERVICEBUS_FULLY_QUALIFIED_NAMESPACE=<namespace>.servicebus.windows.net # Required: Service Bus fully qualified namespaceAZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in productionAZURE_SERVICEBUS_CONNECTION_STRING=Endpoint=sb://... # Alternative to Entra ID authAuthentication
Microsoft Entra Token Credential
csharp
using Azure.Identity;using Azure.Messaging.ServiceBus;
string fullyQualifiedNamespace = "<namespace>.servicebus.windows.net";// Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential>var credential = new DefaultAzureCredential( DefaultAzureCredential.DefaultEnvironmentVariableName);// Or use a specific credential directly in production:// See https://learn.microsoft.com/dotnet/api/overview/azure/identity-readme?view=azure-dotnet#credential-classes// var credential = new ManagedIdentityCredential();await using ServiceBusClient client = new(fullyQualifiedNamespace, credential);Connection String
csharp
string connectionString = "<connection_string>";await using ServiceBusClient client = new(connectionString);ASP.NET Core Dependency Injection
csharp
services.AddAzureClients(builder =>{ builder.AddServiceBusClientWithNamespace("<namespace>.servicebus.windows.net"); builder.UseCredential(new DefaultAzureCredential());});Client Hierarchy
ServiceBusClient├── CreateSender(queueOrTopicName) → ServiceBusSender├── CreateReceiver(queueName) → ServiceBusReceiver├── CreateReceiver(topicName, subName) → ServiceBusReceiver├── AcceptNextSessionAsync(queueName) → ServiceBusSessionReceiver├── CreateProcessor(queueName) → ServiceBusProcessor└── CreateSessionProcessor(queueName) → ServiceBusSessionProcessor
ServiceBusAdministrationClient (separate client for CRUD)Core Workflows
1. Send Messages
csharp
await using ServiceBusClient client = new(fullyQualifiedNamespace, new DefaultAzureCredential());ServiceBusSender sender = client.CreateSender("my-queue");
// Single messageServiceBusMessage message = new("Hello world!");await sender.SendMessageAsync(message);
// Safe batching (recommended)using ServiceBusMessageBatch batch = await sender.CreateMessageBatchAsync();if (batch.TryAddMessage(new ServiceBusMessage("Message 1"))){ // Message added successfully}if (batch.TryAddMessage(new ServiceBusMessage("Message 2"))){ // Message added successfully}await sender.SendMessagesAsync(batch);2. Receive Messages
csharp
ServiceBusReceiver receiver = client.CreateReceiver("my-queue");
// Single messageServiceBusReceivedMessage message = await receiver.ReceiveMessageAsync();string body = message.Body.ToString();Console.WriteLine(body);
// Complete the message (removes from queue)await receiver.CompleteMessageAsync(message);
// Batch receiveIReadOnlyList<ServiceBusReceivedMessage> messages = await receiver.ReceiveMessagesAsync(maxMessages: 10);foreach (var msg in messages){ Console.WriteLine(msg.Body.ToString()); await receiver.CompleteMessageAsync(msg);}3. Message Settlement
csharp
// Complete - removes message from queueawait receiver.CompleteMessageAsync(message);
// Abandon - releases lock, message can be received againawait receiver.AbandonMessageAsync(message);
// Defer - prevents normal receive, use ReceiveDeferredMessageAsyncawait receiver.DeferMessageAsync(message);
// Dead Letter - moves to dead letter subqueueawait receiver.DeadLetterMessageAsync(message, "InvalidFormat", "Message body was not valid JSON");4. Background Processing with Processor
csharp
ServiceBusProcessor processor = client.CreateProcessor("my-queue", new ServiceBusProcessorOptions{ AutoCompleteMessages = false, MaxConcurrentCalls = 2});
processor.ProcessMessageAsync += async (args) =>{ try { string body = args.Message.Body.ToString(); Console.WriteLine($"Received: {body}"); await args.CompleteMessageAsync(args.Message); } catch (Exception ex) { Console.WriteLine($"Error processing: {ex.Message}"); await args.AbandonMessageAsync(args.Message); }};
processor.ProcessErrorAsync += (args) =>{ Console.WriteLine($"Error source: {args.ErrorSource}"); Console.WriteLine($"Entity: {args.EntityPath}"); Console.WriteLine($"Exception: {args.Exception}"); return Task.CompletedTask;};
await processor.StartProcessingAsync();// ... application runsawait processor.StopProcessingAsync();5. Sessions (Ordered Processing)
csharp
// Send session messageServiceBusMessage message = new("Hello"){ SessionId = "order-123"};await sender.SendMessageAsync(message);
// Receive from next available sessionServiceBusSessionReceiver receiver = await client.AcceptNextSessionAsync("my-queue");
// Or receive from specific sessionServiceBusSessionReceiver receiver = await client.AcceptSessionAsync("my-queue", "order-123");
// Session state managementawait receiver.SetSessionStateAsync(new BinaryData("processing"));BinaryData state = await receiver.GetSessionStateAsync();
// Renew session lockawait receiver.RenewSessionLockAsync();6. Dead Letter Queue
csharp
// Receive from dead letter queueServiceBusReceiver dlqReceiver = client.CreateReceiver("my-queue", new ServiceBusReceiverOptions{ SubQueue = SubQueue.DeadLetter});
ServiceBusReceivedMessage dlqMessage = await dlqReceiver.ReceiveMessageAsync();
// Access dead letter metadatastring reason = dlqMessage.DeadLetterReason;string description = dlqMessage.DeadLetterErrorDescription;Console.WriteLine($"Dead letter reason: {reason} - {description}");7. Topics and Subscriptions
csharp
// Send to topicServiceBusSender topicSender = client.CreateSender("my-topic");await topicSender.SendMessageAsync(new ServiceBusMessage("Broadcast message"));
// Receive from subscriptionServiceBusReceiver subReceiver = client.CreateReceiver("my-topic", "my-subscription");var message = await subReceiver.ReceiveMessageAsync();8. Administration (CRUD)
csharp
var adminClient = new ServiceBusAdministrationClient( fullyQualifiedNamespace, new DefaultAzureCredential());
// Create queuevar options = new CreateQueueOptions("my-queue"){ MaxDeliveryCount = 10, LockDuration = TimeSpan.FromSeconds(30), RequiresSession = true, DeadLetteringOnMessageExpiration = true};QueueProperties queue = await adminClient.CreateQueueAsync(options);
// Update queuequeue.LockDuration = TimeSpan.FromSeconds(60);await adminClient.UpdateQueueAsync(queue);
// Create topic and subscriptionawait adminClient.CreateTopicAsync(new CreateTopicOptions("my-topic"));await adminClient.CreateSubscriptionAsync(new CreateSubscriptionOptions("my-topic", "my-subscription"));
// Deleteawait adminClient.DeleteQueueAsync("my-queue");9. Cross-Entity Transactions
csharp
var options = new ServiceBusClientOptions { EnableCrossEntityTransactions = true };await using var client = new ServiceBusClient(connectionString, options);
ServiceBusReceiver receiverA = client.CreateReceiver("queueA");ServiceBusSender senderB = client.CreateSender("queueB");
ServiceBusReceivedMessage receivedMessage = await receiverA.ReceiveMessageAsync();
using (var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)){ await receiverA.CompleteMessageAsync(receivedMessage); await senderB.SendMessageAsync(new ServiceBusMessage("Forwarded")); ts.Complete();}Key Types Reference
| Type | Purpose |
|---|---|
ServiceBusClient | Main entry point, manages connection |
ServiceBusSender | Sends messages to queues/topics |
ServiceBusReceiver | Receives messages from queues/subscriptions |
ServiceBusSessionReceiver | Receives session messages |
ServiceBusProcessor | Background message processing |
ServiceBusSessionProcessor | Background session processing |
ServiceBusAdministrationClient | CRUD for queues/topics/subscriptions |
ServiceBusMessage | Message to send |
ServiceBusReceivedMessage | Received message with metadata |
ServiceBusMessageBatch | Batch of messages |
Best Practices
- Use singletons — Clients, senders, receivers, and processors are thread-safe
- Always dispose — Use
await usingor callDisposeAsync() - Dispose order — Close senders/receivers/processors first, then client
- Use DefaultAzureCredential — Prefer over connection strings for production
- Use processors for background work — Handles lock renewal automatically
- Use safe batching —
CreateMessageBatchAsync()andTryAddMessage() - Handle transient errors — Use
ServiceBusException.Reason - Configure transport — Use
AmqpWebSocketsif ports 5671/5672 are blocked - Set appropriate lock duration — Default is 30 seconds
- Use sessions for ordering — FIFO within a session
Error Handling
csharp
try{ await sender.SendMessageAsync(message);}catch (ServiceBusException ex) when (ex.Reason == ServiceBusFailureReason.ServiceBusy){ // Retry with backoff}catch (ServiceBusException ex){ Console.WriteLine($"Service Bus Error: {ex.Reason} - {ex.Message}");}Related SDKs
| SDK | Purpose | Install |
|---|---|---|
Azure.Messaging.ServiceBus | Service Bus (this SDK) | dotnet add package Azure.Messaging.ServiceBus |
Azure.Messaging.EventHubs | Event streaming | dotnet add package Azure.Messaging.EventHubs |
Azure.Messaging.EventGrid | Event routing | dotnet add package Azure.Messaging.EventGrid |


