Topics, Subscriptions, and Message Filtering
Topics vs Queues
| Feature | Queue | Topic |
|---|---|---|
| Pattern | Point-to-point | Publish/Subscribe |
| Consumers | Single consumer per message | Multiple subscriptions receive copies |
| Use Case | Task distribution | Event broadcasting |
| Filtering | N/A | SQL and Correlation filters |
Architecture — Pub/Sub with Topics
┌─────────────────────┐
│ Subscription: │
┌───▶│ inventory-sub │───▶ Inventory Service
│ │ Filter: eventType= │
│ │ 'OrderCreated' │
│ └─────────────────────┘
┌──────────┐ ┌────────┴──────────┐
│ Producer │───▶│ order-events │ ┌─────────────────────┐
│ (App) │ │ (Topic) │───▶│ Subscription: │
└──────────┘ └────────┬──────────┘ │ billing-sub │
│ │ Filter: amount>100 │
│ └─────────────────────┘
│ ┌─────────────────────┐
└───▶│ Subscription: │
│ notifications-sub │
│ Filter: (all) │
└─────────────────────┘
Prerequisites
| Requirement | Details |
|---|---|
| Service Bus Namespace | Standard or Premium tier |
| Azure CLI | v2.50+ |
| .NET SDK | 8.0+ |
Step 1 — Create a Topic
az servicebus topic create \
--name order-events \
--namespace-name sb-demo-namespace \
--resource-group rg-servicebus-demo \
--default-message-time-to-live P7D

Step 2 — Create Subscriptions
# Subscription for inventory service
az servicebus topic subscription create \
--name inventory-sub \
--topic-name order-events \
--namespace-name sb-demo-namespace \
--resource-group rg-servicebus-demo
# Subscription for billing service
az servicebus topic subscription create \
--name billing-sub \
--topic-name order-events \
--namespace-name sb-demo-namespace \
--resource-group rg-servicebus-demo
# Subscription for notifications
az servicebus topic subscription create \
--name notifications-sub \
--topic-name order-events \
--namespace-name sb-demo-namespace \
--resource-group rg-servicebus-demo

Step 3 — Add SQL Filters
SQL filters evaluate a SQL-like expression against message application properties.
# Remove the default "match all" rule from billing-sub
az servicebus topic subscription rule delete \
--name '$Default' \
--subscription-name billing-sub \
--topic-name order-events \
--namespace-name sb-demo-namespace \
--resource-group rg-servicebus-demo
# Add SQL filter: only messages where amount > 100
az servicebus topic subscription rule create \
--name high-value-orders \
--subscription-name billing-sub \
--topic-name order-events \
--namespace-name sb-demo-namespace \
--resource-group rg-servicebus-demo \
--filter-sql-expression "amount > 100"
Step 4 — Add Correlation Filters
Correlation filters match against message properties efficiently (hash-based lookup).
# Remove default rule from inventory-sub
az servicebus topic subscription rule delete \
--name '$Default' \
--subscription-name inventory-sub \
--topic-name order-events \
--namespace-name sb-demo-namespace \
--resource-group rg-servicebus-demo
# Add correlation filter
az servicebus topic subscription rule create \
--name order-created-filter \
--subscription-name inventory-sub \
--topic-name order-events \
--namespace-name sb-demo-namespace \
--resource-group rg-servicebus-demo \
--correlation-filter application-properties="eventType=OrderCreated"
| Filter Type | Performance | Use Case |
|---|---|---|
| SQL Filter | Slower (expression evaluation) | Complex conditions, ranges, LIKE |
| Correlation Filter | Faster (hash match) | Exact property matching |
| True Filter (default) | N/A | Receive all messages |
Step 5 — Send Messages to a Topic
using Azure.Messaging.ServiceBus;
const string connectionString = "<YOUR_CONNECTION_STRING>";
const string topicName = "order-events";
await using var client = new ServiceBusClient(connectionString);
ServiceBusSender sender = client.CreateSender(topicName);
// High-value order (reaches billing-sub and notifications-sub)
var highValueOrder = new ServiceBusMessage("High value order: $250")
{
ApplicationProperties =
{
{ "eventType", "OrderCreated" },
{ "amount", 250 }
}
};
await sender.SendMessageAsync(highValueOrder);
// Low-value order (only reaches notifications-sub)
var lowValueOrder = new ServiceBusMessage("Low value order: $25")
{
ApplicationProperties =
{
{ "eventType", "OrderCreated" },
{ "amount", 25 }
}
};
await sender.SendMessageAsync(lowValueOrder);
Console.WriteLine("Messages sent to topic.");
Step 6 — Receive from a Subscription
using Azure.Messaging.ServiceBus;
const string connectionString = "<YOUR_CONNECTION_STRING>";
const string topicName = "order-events";
const string subscriptionName = "billing-sub";
await using var client = new ServiceBusClient(connectionString);
ServiceBusReceiver receiver = client.CreateReceiver(topicName, subscriptionName);
IReadOnlyList<ServiceBusReceivedMessage> messages =
await receiver.ReceiveMessagesAsync(maxMessages: 10, maxWaitTime: TimeSpan.FromSeconds(5));
foreach (var msg in messages)
{
Console.WriteLine($"Billing received: {msg.Body} | Amount: {msg.ApplicationProperties["amount"]}");
await receiver.CompleteMessageAsync(msg);
}

Real-World Scenario — Order Events
When an order is placed, multiple services need to react independently:
- Inventory Service — reserves stock (filter:
eventType = 'OrderCreated') - Billing Service — charges card for high-value orders (filter:
amount > 100) - Notification Service — sends confirmation email (no filter, receives all)
Each subscription processes messages at its own pace without blocking others.
Summary
- ✅ Understood Topics vs Queues (pub/sub pattern)
- ✅ Created a topic with multiple subscriptions
- ✅ Applied SQL and Correlation filters
- ✅ Sent messages with application properties
- ✅ Received filtered messages from subscriptions
Previous: Send and Receive Messages with .NET SDK