Migration from Microsoft.Azure.ServiceBus to Azure.Messaging.ServiceBus

Viewed 1539

I'm trying to update this function to make use of Azure.Messaging.ServiceBus and drop Microsoft.Azure.ServiceBus altogether, however cant seem to find any resources for this. Anybody knows how to send a message to a topic using this package?

The older function is:

  public async Task SendMessageToServiceBusTopic<T>(T request, string topicSubName, string submissionNumber)
    {
        ServiceBusConnectionStringBuilder serviceBusConnectionStringBuilder =
            new ServiceBusConnectionStringBuilder(settings.ServiceBusConnectionString)
            {
                EntityPath = settings.ServiceBusTopic
            };

     
            TopicClient topicClient = new TopicClient(serviceBusConnectionStringBuilder);

            byte[] bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(request));

            await topicClient.SendAsync(new Message(bytes)
            {
                CorrelationId = context.CorrelationId,
                Label=topicSubName,
                UserProperties = { new KeyValuePair<string, object>("TrackingId", submissionNumber) }
            });          
    }

So far I have managed:

Am i headed in the right direction?

  public async Task SendMessageToServiceBusTopic<T>(T request, string topicSubName, string submissionNumber)
    {
        ServiceBusClient client = new ServiceBusClient(settings.ServiceBusConnectionString);
        ServiceBusSender s = client.CreateSender(settings.ServiceBusTopic);


            byte[] bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(request));
            await s.SendMessageAsync(new ServiceBusMessage(bytes));
      }
3 Answers

While you can construct a Service Bus Client each time, it's not ideal. Assuming you're using the latest In-Proc SDK, you can use one of these options:

[FunctionName("PublishToTopic")]
public static async Task Run(
    [TimerTrigger("0 */5 * * * *")] TimerInfo myTimer,
    [ServiceBus("<topic_name>", Connection = "<connection_name>")] IAsyncCollector<ServiceBusMessage> collector)
{
    await collector.AddAsync(new ServiceBusMessage(new BinaryData($"Message 1 added at: {DateTime.Now}")));
    await collector.AddAsync(new ServiceBusMessage(new BinaryData($"Message 2 added at: {DateTime.Now}")));
}

Alternatively,

[FunctionName("PublishWithSender"]
public static async Task Run(
    [TimerTrigger("0 */5 * * * *")] TimerInfo myTimer,
    [ServiceBus("<topic_name>", Connection = "<connection_name>")] ServiceBusSender sender)
{
    await sender.SendMessagesAsync(new[]
    {
        new ServiceBusMessage(new BinaryData($"Message 1 added at: {DateTime.Now}")),
        new ServiceBusMessage(new BinaryData($"Message 2 added at: {DateTime.Now}"))
    });
}

For Isolated Worker SDK it's somewhat different. See this post for details.

you're in the right direction. There's no easy migration tool/sample as they are different libraries dealing with the same service (Azure Service Bus).

I was in the same situation like you (trying to migrate to Azure.Messaging.ServiceBus which is now the recommended NuGet from Microsoft).

You can have a look at the migration guide from their Github to have an idea of how to make the transition smoother: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/servicebus/Azure.Messaging.ServiceBus/MigrationGuide.md

/!\ IQueueClient and ITopicClient (both coming from Microsoft.Azure.ServiceBus) have been merged into a single object called ServiceBusSender.

Why ? to make our life easier ! Indeed, under the hood, ServiceBusSender now takes care of the process of sending the message, especially since a queue and a topic cannot have the same name if I'm not wrong.

Here's a sample code of mine to send a message using the new library:

    /// <summary>
    /// Send a message by serializing as JSON the object in input. 
    /// </summary>
    /// <param name="queueOrTopicName">Name of the queue or topic where the message will be sent.</param>
    /// <param name="messageToSend">Any C# object</param>
    /// <returns></returns>
    public async Task SendMessage(string queueOrTopicName, object messageToSend)
    {
        //ServiceBusSender should not be disposed (according to the documentation, Github, etc.)
        ServiceBusClient client = new ServiceBusClient(connectionString: "Your-ServiceBus-ConnectionString");
        ServiceBusSender sender = client.CreateSender(_queueOrTopicName);

        // create a message that we can send. UTF-8 encoding is used when providing a string.
        ServiceBusMessage message = BuildServiceBusMessage(messageToSend);

        // Finally send the message
        await sender.SendMessageAsync(message);
    }

    private ServiceBusMessage BuildServiceBusMessage<T>(T entity)
    {
        string serializedMessage = JsonConvert.SerializeObject(entity); // Still using Newtonsoft.Json but I don't see any obstacles of using System.Text.Json.
        ServiceBusMessage message = new ServiceBusMessage(serializedMessage)
        {
            MessageId = Guid.NewGuid().ToString(),
            ContentType = "application/json; charset=utf-8",
        };

        return message;
    }

If you have any needs to do Dependency Injection (to re-use the same ServiceBusClient object and avoiding instanciating ServiceBusClient for each message you want to send for example), you can refer to this stackoverflow that I discovered this week

How to register ServiceBusClient for dependency injection?

Related