0 Comments

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 filterscome 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.

Vote on HN
comments powered by Disqus