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

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));
}

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.)
using ServiceBusClient client = new ServiceBusClient(connectionString: "Your-ServiceBus-ConnectionString");
using 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

Issue (maybe) with the code for putting message on the queue

I have this code to put message on the queue
public class AzureQueueService : IAzureQueueService
{
private readonly IConfiguration _config;
private static string azureServiceBusString = null;
public AzureQueueService(IConfiguration config)
{
_config = config;
azureServiceBusString = "connString";
}
public async Task SendMessageAsync<T>(T serviceBusMessage, string queueName)
{
// since ServiceBusClient implements IAsyncDisposable we create it with "await using"
await using var queueClient = new ServiceBusClient(azureServiceBusString);
// create the sender
ServiceBusSender sender = queueClient.CreateSender(queueName);
string messageBody = JsonSerializer.Serialize(serviceBusMessage);
var message = new ServiceBusMessage(Encoding.UTF8.GetBytes(messageBody));
// send the message
await sender.SendMessageAsync(message);
}
}
In my startup file I am doing this
services.AddTransient<IAzureQueueService, Core.Services.AzureQueueService>();
And I am using it like this
var queue = new AzureQueueService(config);
await queue.SendMessageAsync(message, "emailqueue");
Could this lead to memory leakage? I mean should I instantiate the ServiceBusClient in the constructor?
Yes, I'd be looking to set up dependency injection for the ServiceBusClient. As per the docs here (for 7.8.x)
The ServiceBusClient, senders, receivers, and processors are safe to cache and use as a singleton for the lifetime of the application, which is best practice when messages are being sent or received regularly. They are responsible for efficient management of network, CPU, and memory use, working to keep usage low during periods of inactivity.
Also, see Best Practises for performance improvements using Service Bus Messaging

Returned Azure service bus queue sequence number different in my consumer than what was returned in the producer and shown in the Azure portal?

When I create a scheduled service bus message, both in Azure Portal and in my app using the Service bus producer code (below) and I receive a sequence number. I save it in my db.
Problem - When my Service bus consumer code is triggered by the dequeue of the scheduled message the sequence number is different than the one that was initially given to me by both the service bus producer code and through the Azure portal.
Shown here, where '13' is the sequnce number shown in Azure Portal screen.
Here is the code that receives the scheduled message and you can see the sequence number is different!
Here is my consumer code (don't think it matters)
private async Task MessageHandler(ProcessMessageEventArgs args)
{
string body = args.Message.Body.ToString();
JObject jsonObject = JObject.Parse(body);
var eventStatus = (string)jsonObject["EventStatus"];
await args.CompleteMessageAsync(args.Message);
// fetch row here by sequence number
// edit some data from entity, then save
int result = await dbContext.SaveChangesAsync();
}
Here is my producer code
public async Task<long> SendMessage(string messageBody, DateTimeOffset scheduledEnqueueTimeUtc)
{
await using (ServiceBusClient client = new ServiceBusClient(_config["ServiceBus:Connection"]))
{
ServiceBusSender sender = client.CreateSender(_config["ServiceBus:Queue"]);
ServiceBusMessage message = new ServiceBusMessage(messageBody);
var sequenceNumber = await sender.ScheduleMessageAsync(message, scheduledEnqueueTimeUtc);
return sequenceNumber;
}
}
From the documentation:
The SequenceNumber for a scheduled message is only valid while the message is in this state. As the message transitions to the active state, the message is appended to the queue as if had been enqueued at the current instant, which includes assigning a new SequenceNumber.
This is the code on my side:
using System;
using System.Threading;
using System.Threading.Tasks;
using Azure.Messaging.ServiceBus;
namespace ConsoleApp3
{
class Program
{
static string connectionString = "xxxxxx";
static string queueName = "myqueue";
static ServiceBusClient client;
static ServiceBusProcessor processor;
static async Task Main(string[] args)
{
client = new ServiceBusClient(connectionString);
processor = client.CreateProcessor(queueName, new ServiceBusProcessorOptions());
try
{
processor.ProcessMessageAsync += MessageHandler;
processor.ProcessErrorAsync += ErrorHandler;
await processor.StartProcessingAsync();
Console.WriteLine("Wait for a minute and then press any key to end the processing");
Console.ReadKey();
Console.WriteLine("\nStopping the receiver...");
await processor.StopProcessingAsync();
Console.WriteLine("Stopped receiving messages");
}
finally
{
await processor.DisposeAsync();
await client.DisposeAsync();
}
}
static async Task MessageHandler(ProcessMessageEventArgs args)
{
string body = args.Message.Body.ToString();
Console.WriteLine($"Received: {body}");
Console.WriteLine($"ID: {args.Message.MessageId}");
await args.CompleteMessageAsync(args.Message);
}
static Task ErrorHandler(ProcessErrorEventArgs args)
{
Console.WriteLine(args.Exception.ToString());
return Task.CompletedTask;
}
}
}
And it seems no problem on my side:
Message Id changed should be the message be thrown back by some reasons.

What is alternate of ServiceBus MessagingFactory in .Net Core?

While converting my project from .Net framework 4.7 to .Net core 2.1, I'm facing issue with Servicebus MessagingFactory. I don't see any MessagingFactory class in new nuget package Microsoft.Azure.ServiceBus for .Net core.
My .Net framework 4.7 Code
private static readonly string messagingConnectionString = Environment.GetEnvironmentVariable("ServiceBusConnection");
private static Lazy<MessagingFactory> lazyMessagingFactory = new Lazy<MessagingFactory>(() =>
{
return MessagingFactory.CreateFromConnectionString(messagingConnectionString);
});
public static MessagingFactory MessagingFactory
{
get
{
return lazyMessagingFactory.Value;
}
}
public static MessagingFactory EventHubMessageFactory
{
get
{
return lazyEventhubMessagingFactory.Value;
}
}
public async Task SendMessageToQueueAsync(string queueName, string message)
{
QueueClient queueClient = MessagingFactory.CreateQueueClient(queueName);
BrokeredMessage brokeredMessage = new BrokeredMessage(new MemoryStream(Encoding.UTF8.GetBytes(message)), true);
await queueClient.SendAsync(brokeredMessage);
}
It was a best practices for high performance application, Also I have many queues under single service bus namespace and I push message based on configuration. I don't want to create QueueClient object in every request and don't want to maintain connection string for every queue.
What is alternate of MessagingFactory in .Net Core?
There are major changes when you are migrating .NetFramework code into .Netcore, you can see Guide for migrating to Azure.Messaging.ServiceBus from Microsoft.Azure.ServiceBus
Example below
static void Main(string[] args)
{
string connectionString = "<connection_string>";
string queueName = "<queue_name>";
// since ServiceBusClient implements IAsyncDisposable we create it with "await using"
var client = new ServiceBusClient(connectionString);
// create the sender
ServiceBusSender sender = client.CreateSender(queueName);
for (int i = 0; i < 10; i++)
{
// create a message that we can send. UTF-8 encoding is used when providing a string.
ServiceBusMessage message = new ServiceBusMessage($"Hello world {i}!");
// send the message
sender.SendMessageAsync(message).GetAwaiter().GetResult();
}
sender.DisposeAsync().GetAwaiter().GetResult();
client.DisposeAsync().GetAwaiter().GetResult();
}
https://github.com/MicrosoftDocs/azure-docs/issues/46830
https://github.com/Azure/azure-service-bus-dotnet/issues/556
While MessagingFactory is gone, the idea of connection pooling and sharing connections is there. When you create your clients, passing a connection will reuse it. When passing a connection string, will cause clients to establish a new connection.
So you can manually create a ServiceBusConnection or reuse one of an existing client. You can pass the connection object in the constructors of the clients you create. Take care not to close a connection accidentally, e.g. by closing the client that created it.

Does the WindowsAzure.Storage library for Xamarin use NSUrlSession for file uploads?

Problem Statement: We have a requirement to upload log data to Azure Storage from a Xamarin.IOS application. The logs are not created by the user of the application, and there's no constraint on the user to keep the application open for any amount of time after the logs are generated. We want to reliably upload our logs with a couple points in mind:
The user might send the app into the background
The file sizes can be up to 15MB
We don't care when we get them. We're open to scheduling a task for this.
In looking at potential solutions to this problem, the Xamarin documentation states that in iOS7+:
NSURLSession allows us to create tasks to:
Transfer content through network and device interruptions.
Upload and download large files ( Background Transfer Service ).
So it seems like NSURLSession is a good candidate for this sort of work, but I wonder if I am reinventing the wheel. Does the WindowsAzure.Storage client library respect app backgrounding with an upload implementation based on NSURLSession, or if I want to upload the data in the background, is it necessary to upload to an intermediate server I control with a POST method, and then relay data to Azure Storage? There doesn't seem to be any indication from the public Azure documentation that uploads can be done via scheduled task.
I got this working. I've simplified the classes and methods into a single method. Only the necessities are here.
public void UploadFile(File playbackFile)
{
/// Specify your credentials
var sasURL = "?<the sastoken>";
/// Azure blob storage URL
var storageAccount = "https://<yourstorageaccount>.blob.core.windows.net/<your container name>";
/// specify a UNIQUE session name
var configuration =
NSUrlSessionConfiguration.CreateBackgroundSessionConfiguration("A background session name");
/// create the session with a delegate to recieve callbacks and debug
var session = NSUrlSession.FromConfiguration(
configuration,
new YourSessionDelegate(),
new NSOperationQueue());
/// Construct the blob endpoint
var url = $"{storageAccount}/{playbackFile.Name}{sasURL}";
var uploadUrl = NSUrl.FromString(url);
/// Add any headers for Blob PUT. x-ms-blob-type is REQUIRED
var dic = new NSMutableDictionary();
dic.Add(new NSString("x-ms-blob-type"), new NSString("BlockBlob"));
/// Create the request with NSMutableUrlRequest
/// A default NSUrlRequest.FromURL() is immutable with a GET method
var request = new NSMutableUrlRequest(uploadUrl);
request.Headers = dic;
request.HttpMethod = "PUT";
/// Create the task
var uploadTask = session.CreateUploadTask(
request,
NSUrl.FromFilename(playbackFile.FullName));
/// Start the task
uploadTask.Resume();
}
/// Delegate to recieve callbacks. Implementations are omitted for brevity
public class YourSessionDelegate: NSUrlSessionDataDelegate
{
public override void DidBecomeInvalid(NSUrlSession session, NSError error)
{
Console.WriteLine(error.Description);
}
public override void DidSendBodyData(NSUrlSession session, NSUrlSessionTask task, long bytesSent, long totalBytesSent, long totalBytesExpectedToSend)
{
Console.WriteLine(bytesSent);
}
public override void DidReceiveData(NSUrlSession session, NSUrlSessionDataTask dataTask, NSData data)
{
Console.WriteLine(data);
}
public override void DidCompleteWithError(NSUrlSession session, NSUrlSessionTask task, NSError error)
{
var uploadTask = task as NSUrlSessionUploadTask;
Console.WriteLine(error?.Description);
}
public override void DidReceiveResponse(NSUrlSession session, NSUrlSessionDataTask dataTask, NSUrlResponse response, Action<NSUrlSessionResponseDisposition> completionHandler)
{
Console.WriteLine(response);
}
public override void DidFinishEventsForBackgroundSession(NSUrlSession session)
{
using (AppDelegate appDelegate = UIApplication.SharedApplication.Delegate as AppDelegate)
{
var handler = appDelegate.BackgroundSessionCompletionHandler;
if (handler != null)
{
appDelegate.BackgroundSessionCompletionHandler = null;
handler();
}
}
}
}
Helpful documentation:
https://learn.microsoft.com/en-us/rest/api/storageservices/put-blob
https://developer.apple.com/documentation/foundation/nsmutableurlrequest/1408793-setvalue
https://learn.microsoft.com/en-us/dotnet/api/foundation.insurlsessiontaskdelegate?view=xamarin-ios-sdk-12
Hopefully someone finds this useful and spends less time on this than I did. Thanks #SushiHangover for pointing me in the right direction.

Azure Durable Function: JsonSerializationException by passing complex object from trigger to orchestrator

I have an azure function with EventHubTrigger:
[FunctionName("TradesDataProcessStarterEh")]
public static async Task TradesDataProcessStarterEh([EventHubTrigger("aeehrobotronapiintegrationdev", Connection = "EventHubConnectionString", ConsumerGroup = "$Default")]
EventData eventData, PartitionContext partitionContext, [OrchestrationClient] DurableOrchestrationClient starter, ILogger log)
{
if (partitionContext.PartitionId != "1")
return;
var orchestrationId = await starter.StartNewAsync("O_ProcessTradesFromEventHub", eventData);
await partitionContext.CheckpointAsync();
}
The orchestrator function is receiving then the eventData:
[FunctionName("O_ProcessTradesFromEventHub")]
public static async Task ProcessTradesFromEventHub([OrchestrationTrigger] DurableOrchestrationContext context,
ILogger log)
{
if (!context.IsReplaying)
Console.WriteLine("O_ProcessTradesFromEventHub is triggered");
var eventData = context.GetInput<EventData>();
//do stuff...
}
But by execution of context.GetInput() I get an exception:
Function 'O_ProcessTradesFromEventHub (Orchestrator)' failed with an error. Reason: Newtonsoft.Json.JsonSerializationException: Unable to find a constructor to use for type Microsoft.Azure.EventHubs.EventData. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'Body', line 1, position 81.
I can think of 3 possible solutions that you can try:
1 - Wrap EventData in your own class with a constructor (possibly via inheritance?).
2 - Try casting to object, doubt this will work but, but worth a try as it's a simple fix.
3 - Build your own DTO (Data Transfer Object) to transform EventData to <your class> and then pass <your class> to the orchestration.
I think (3) is the cleanest solution and you have full control over what you pass, unfortunately it is likely the least performant and most tedious.
Good luck!
Use LINQ to JSON - a year later but hopefully it'll save somebody else some time.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public static async Task Run(
[OrchestrationTrigger] DurableOrchestrationContext context, ILogger log) {
var eventData = context.GetInput<JObject>();
log.LogInformation ($"Executing tasks with eventData = {eventData}");
string step = (string)eventData.SelectToken("Step");
log.LogInformation ($"Step = {step}");
}

Resources