Posted in:

Azure Service Bus topics allow multiple subscribers to receive the same messages. So if we post an OrderReceived message to a topic, then one subscriber might send an order confirmation email, while another subscriber to the same event might handle payments.

The way this is done is that you create multiple “subscriptions” on the topic, and then you can listen for messages on those subscriptions, in the same way that you would listen for messages on a regular queue.

But what if your subscription is only interested in a subset of messages that are posted to that topic? Well, this is where filters come in. When we create a subscription, we can specify the properties of the messages we are interested in.

Here’s a simple example of how to achieve this. First of all, let’s connect to a Service Bus namespace (I’m using LinqPad’s Util.GetPassword as a convenient way to keep my connection string private), and create our topic if it doesn’t already exist.

string connectionString = Util.GetPassword("Test Azure Service Bus Connection String");

const string topicName = "ExampleTestTopic";
const string subscriptionName = "AllMessages";
const string sub1Name = "Filtered1";
const string sub2Name = "Filtered2";

// PART 1 - CREATE THE TOPIC
Console.WriteLine("Creating the topic");
var namespaceManager =
    NamespaceManager.CreateFromConnectionString(connectionString);
if (!namespaceManager.TopicExists(topicName))
{
    // Configure Topic Settings.
    var td = new TopicDescription(topicName);
    td.MaxSizeInMegabytes = 1024;
    td.DefaultMessageTimeToLive = TimeSpan.FromMinutes(5);
    
    namespaceManager.CreateTopic(td);
}

Now we’ll create some subscriptions. The first one will receive all messages, while the other two will use filters. The way to set up filters is to use the SqlFilter class. This allows you to specify custom expressions to decide whether the message should be received. These are in a SQL-like syntax, and you can use custom or built-in message properties. You can’t however look at the message body, so you need to make sure that the message properties contains enough information for the filter to make its decision.

In this example, our first filtered subscription will only receive messages whose custom “From” property ends with “Smith”. We do this with a filter value of "From LIKE '%Smith'" And the second filtered subscription will look to see if the built-in Label property of the brokered message has the value “important”, using the filter "sys.Label='important'".

In this example, I’m deleting and recreating the filtered subscriptions every time to make testing easier:

if (!namespaceManager.SubscriptionExists(topicName, subscriptionName))
{
    namespaceManager.CreateSubscription(topicName, subscriptionName);
}
if (namespaceManager.SubscriptionExists(topicName, sub1Name))
{
    Console.WriteLine("Deleting subscription {0}", sub1Name);
    namespaceManager.DeleteSubscription(topicName, sub1Name);
}
Console.WriteLine("Creating subscription {0}", sub1Name);
namespaceManager.CreateSubscription(topicName, sub1Name, new SqlFilter("From LIKE '%Smith'"));
if (namespaceManager.SubscriptionExists(topicName, sub2Name))
{
    Console.WriteLine("Deleting subscription {0}", sub2Name);
    namespaceManager.DeleteSubscription(topicName, sub2Name);
}
Console.WriteLine("Creating subscription {0}", sub2Name);
namespaceManager.CreateSubscription(topicName, sub2Name, new SqlFilter("sys.Label='important'"));

Now let’s send some messages. I’ll send three, with one marked as “important”, and two that will match our “Smith” filter.

var body = "Hello World";
var message1 = new BrokeredMessage(body);
message1.Properties["From"] = "Ian Wright";

var message2 = new BrokeredMessage("Second message");
message2.Properties["From"] = "Alan Smith";
message2.Label = "important";

var message3 = new BrokeredMessage("Third message");
message3.Properties["From"] = "Kelly Smith";
message3.Label = "information";

var client = TopicClient.CreateFromConnectionString(connectionString, topicName);
client.Send(message1);
client.Send(message2);
client.Send(message3);

Finally, let’s listen to our subscriptions. We should get all three messages from the all messages subscription, and two on the “Smith” subscription (sub1) and one on the “important” subscription (sub2).

var options = new OnMessageOptions();

var subClient =
    SubscriptionClient.CreateFromConnectionString(connectionString, topicName, subscriptionName);
subClient.OnMessage(m => MessageReceived("ALL", m), options);


var subClient1 =
    SubscriptionClient.CreateFromConnectionString(connectionString, topicName, sub1Name);

subClient1.OnMessage(m => MessageReceived("SUB1", m), options);

var subClient2 =
    SubscriptionClient.CreateFromConnectionString(connectionString, topicName, sub2Name);

subClient2.OnMessage(m => MessageReceived("SUB2", m), options);

Of course, you can get away without using filters at all, if you just set up plain subscriptions and only respond to messages of interest in your handlers. But using the filters will reduce network traffic and save unnecessary work processing messages that aren’t of interest.

A full code example as a LINQPad Gist is available here.

Comments

Comment by Avinava

This was really an easy and effective way to set u topics and filtered subscriptions via code. Much appreciate.

Avinava
Comment by ori

i am using .netFramework 4.5.1 with Microsoft.ServiceBus to subscribe to topics, but I don't have the NamespaceManager.
any idea how I can create rules for subscription?

ori
Comment by ori

thanks for the quick reply.
I found the NamespaceManager.
another question though: (if you dont mind)
my client is WebForm running on framework 4.5.1, and my server is .netcore running on azure.
the servicebus is running on azure.
when I try to create a subscription from the client it gives me an error:
The incoming request is not recognized as a namespace policy put request.
creating a subscription from the server works.
any ieda?

ori
Comment by Mark Heath

Sorry, not sure what's going on here. Would be best to ask this question on StackOverflow or the Azure ServiceBus SDK GitHub

Mark Heath