diff --git a/sdk/servicebus/Azure.Messaging.ServiceBus/MigrationGuide.md b/sdk/servicebus/Azure.Messaging.ServiceBus/MigrationGuide.md index 16ff0c8523d6c..0939a1058d89a 100644 --- a/sdk/servicebus/Azure.Messaging.ServiceBus/MigrationGuide.md +++ b/sdk/servicebus/Azure.Messaging.ServiceBus/MigrationGuide.md @@ -1,8 +1,8 @@ # Guide for migrating to Azure.Messaging.ServiceBus from Microsoft.Azure.ServiceBus -This guide is intended to assist in the migration to version 7 of the Service Bus client library from version 4. It will focus on side-by-side comparisons for similar operations between the v7 package, [`Azure.Messaging.ServiceBus`](https://www.nuget.org/packages/Azure.Messaging.ServiceBus/) and v4 package, [`Microsoft.Azure.ServiceBus`](https://www.nuget.org/packages/Microsoft.Azure.ServiceBus/). +This guide is intended to assist in the migration to version 7 of the Service Bus client library [`Azure.Messaging.ServiceBus`](https://www.nuget.org/packages/Azure.Messaging.ServiceBus/) from version 4 of [`Microsoft.Azure.ServiceBus`](https://www.nuget.org/packages/Microsoft.Azure.ServiceBus/). It will focus on side-by-side comparisons for similar operations between the two packages. -Familiarity with the v4 client library is assumed. For those new to the Service Bus client library for .NET, please refer to the [README](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/servicebus/Azure.Messaging.ServiceBus/README.md) and [Service Bus samples](https://github.com/Azure/azure-sdk-for-net/tree/master/sdk/servicebus/Azure.Messaging.ServiceBus/samples) for the v7 library rather than this guide. +Familiarity with the `Microsoft.Azure.ServiceBus` library is assumed. For those new to the Service Bus client library for .NET, please refer to the [README](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/servicebus/Azure.Messaging.ServiceBus/README.md) and [Service Bus samples](https://github.com/Azure/azure-sdk-for-net/tree/master/sdk/servicebus/Azure.Messaging.ServiceBus/samples) for the `Azure.Messaging.ServiceBus` library rather than this guide. ## Table of contents @@ -11,13 +11,9 @@ Familiarity with the v4 client library is assumed. For those new to the Service - [Package and namespaces](#package-and-namespaces) - [Client hierarchy](#client-hierarchy) - [Client constructors](#client-constructors) - - [Creating sender and receiver](#creating-sender-and-receiver) - [Sending messages](#sending-messages) - [Receiving messages](#receiving-messages) - [Working with sessions](#working-with-sessions) -- [Migration samples](#migration-samples) - - [Sending and receiving a message](#sending-and-receiving-a-message) - - [Sending and receiving a batch of messages](#sending-and-receiving-a-batch-of-messages) - [Additional samples](#additional-samples) ## Migration benefits @@ -28,9 +24,9 @@ There were several areas of consistent feedback expressed across the Azure clien To try and improve the development experience across Azure services, including Service Bus, a set of uniform [design guidelines](https://azure.github.io/azure-sdk/general_introduction.html) was created for all languages to drive a consistent experience with established API patterns for all services. A set of [.NET-specific guidelines](https://azure.github.io/azure-sdk/dotnet_introduction.html) was also introduced to ensure that .NET clients have a natural and idiomatic feel that mirrors that of the .NET base class libraries. Further details are available in the guidelines for those interested. -The modern Service Bus client library provides the ability to share in some of the cross-service improvements made to the Azure development experience, such as using the new `Azure.Identity` library to share a single authentication between clients and a unified diagnostics pipeline offering a common view of the activities across each of the client libraries. +The new Service Bus library `Azure.Messaging.ServiceBus` provides the ability to share in some of the cross-service improvements made to the Azure development experience, such as using the new `Azure.Identity` library to share a single authentication between clients and a unified diagnostics pipeline offering a common view of the activities across each of the client libraries. -While we believe that there is significant benefit to adopting the modern version of the Service Bus library, it is important to be aware that the legacy version has not been officially deprecated. It will continue to be supported with security and bug fixes as well as receiving some minor refinements. However, in the near future it will not be under active development and new features are unlikely to be added. There is no guarantee of feature parity between the modern and legacy client library versions. +While we believe that there is significant benefit to adopting the new Service Bus library `Azure.Messaging.ServiceBus`, it is important to be aware that the previous two versions `WindowsAzure.ServiceBus` and `Microsoft.Azure.ServiceBus` have not been officially deprecated. They will continue to be supported with security and bug fixes as well as receiving some minor refinements. However, in the near future they will not be under active development and new features are unlikely to be added to them. ## General changes @@ -42,210 +38,224 @@ In the case of Service Bus, the modern client libraries have packages and namesp ### Client hierarchy -In the interest of simplifying the API surface we've made a single top level client called `ServiceBusClient`, rather than one for each of queue, topic and subscription: +In the interest of simplifying the API surface we've made a single top level client called `ServiceBusClient`, rather than one for each of queue, topic, subscription and session. This acts as the single entry point in contrast with multiple entry points from before. You can create senders and receivers from this client to the queue/topic/subscription/session of your choice and start sending/receiving messages. -### Client constructors +#### Approachability +By having a single entry point, the `ServiceBusClient` helps with the discoverability of the API as you can explore all available features through methods from a single client, as opposed to searching through documentation or exploring namespace for the types that you can instantiate. Whether sending or receiving, using sessions or not, you will start your applications by constructing the same client. + +#### Consistency +We now have methods with similar names, signature and location to create senders and receivers. This provides consistency and predictability on the various features of the library. We have attempted to have the session/non-session usage be as seamless as possible. This allows you to make less changes to your code when you want to move from sessions to non-sessions or the other way around. + +#### Connection Pooling +By using a single top-level client, we can implicitly share a single AMQP connection for all operations that an application performs. In the previous library `Microsoft.Azure.ServiceBus`, connection sharing was implicit when using the `SessionClient`, but when using other clients, senders or receivers, you would need to explicitly pass in a `ServiceBusConnection` object in order to share a connection. -| In v4 | Equivalent in v7 | Sample | -|-------------------------------------------------------|-----------------------------------------------------------------|--------| -| `new QueueClient()` or `new TopicClient()` or `new SubscriptionClient()` or `new SessionClient()` | `new ServiceBusClient()` | [Authenticate with connection string](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/servicebus/Azure.Messaging.ServiceBus/tests/Samples/Sample01_HelloWorld.cs#L177) | -| `new QueueClient(..., ITokenProvider)` or `new TopicClient(..., ITokenProvider)` or `new SubscriptionClient(..., ITokenProvider)` or `new SessionClient(..., ITokenProvider)` | `new ServiceBusClient(..., TokenCredential)` | [Authenticate with client secret credential](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/servicebus/Azure.Messaging.ServiceBus/tests/Samples/Sample01_HelloWorld.cs#L165) +By making this connection sharing be implicit to a `ServiceBusClient` instance, we can help ensure that applications will not use multiple connections unless they explicitly opt in by creating multiple `ServiceBusClient` instances. The mental model of 1 client - 1 connection is more intuitive than 1 client/sender/receiver - 1 connection. + -### Creating sender and receiver +### Client constructors -| In v4 | Equivalent in v7 | Sample | -|-------------------------------------------------------|-----------------------------------------------------------------|--------| -`new MessageSender()` | `ServiceBusClient.CreateSender()` | [Create the sender](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/servicebus/Azure.Messaging.ServiceBus/samples/Sample01_HelloWorld.md) | -`new MessageReceiver()` | `ServiceBusClient.CreateReceiver()` | [Create the receiver](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/servicebus/Azure.Messaging.ServiceBus/samples/Sample01_HelloWorld.md) | -`new MessageReceiver()` | `ServiceBusClient.CreateProcessor()` | [Create the processor](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/servicebus/Azure.Messaging.ServiceBus/samples/Sample04_Processor.md) | -| `SessionClient.AcceptMessageSessionAsync()` | `ServiceBusClient.CreateSessionReceiverAsync()` | [Create the session receiver](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/servicebus/Azure.Messaging.ServiceBus/samples/Sample03_SendReceiveSessions.md) -| `SessionClient.AcceptMessageSessionAsync()` | `ServiceBusClient.CreateSessionProcessor()` | [Create the session processor](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/servicebus/Azure.Messaging.ServiceBus/samples/Sample05_SessionProcessor.md) +While we continue to support connection strings when constructing a client, the main difference is when using Azure Active Directory. We now use the new [Azure.Identity](https://www.nuget.org/packages/Azure.Identity) library to share a single authentication solution between clients of different Azure services. -### Sending messages +```cs +// Create a ServiceBusClient that will authenticate through Active Directory +string fullyQualifiedNamespace = "yournamespace.servicebus.windows.net"; +ServiceBusClient client = new ServiceBusClient(fullyQualifiedNamespace, new DefaultAzureCredential()); -The v4 client allowed for sending a single message or a list of messages, which had the potential to fail unexpectedly if the maximum allowable size was exceeded. v7 aims to prevent this by allowing you to first create a batch of messages using `CreateMessageBatchAsync` and then attempt to add messages to that using `TryAddMessage()`. If the batch accepts a message, you can be confident that it will not violate size constraints when calling Send to send the batch. v7 still allows sending a single message and sending an `IEnumerable` of messages, though using the `IEnumerable` overload has the same risks as V4. +// Create a ServiceBusClient that will authenticate using a connection string +string connectionString = "Endpoint=sb://yournamespace.servicebus.windows.net/;SharedAccessKeyName=your-key-name;SharedAccessKey=your-key"; +ServiceBusClient client = new ServiceBusClient(connectionString); +``` -| In v4 | Equivalent in v7 | Sample | -|------------------------------------------------|------------------------------------------------------------------|--------| -| `QueueClient.SendAsync(Message)` or `MessageSender.SendAsync(Message)` | `ServiceBusSender.SendMessageAsync(ServiceBusMessage)` | [Send a message](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/servicebus/Azure.Messaging.ServiceBus/samples/Sample01_HelloWorld.md#sending-and-receiving-a-message) | -| `QueueClient.SendAsync(IList)` or `MessageSender.SendAsync(IList)` | `messageBatch = ServiceBusSender.CreateMessageBatchAsync()` `messageBatch.TryAddMessage(ServiceBusMessage)` `ServiceBusSender.SendMessagesAsync(messageBatch)` or `ServiceBusSender.SendMessagesAsync(IEnumerable)` | [Send a batch of messages](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/servicebus/Azure.Messaging.ServiceBus/samples/Sample01_HelloWorld.md#sending-and-receiving-a-batch-of-messages) | +### Sending messages -### Receiving messages +Previously, in `Microsoft.Azure.ServiceBus`, you could send messages either by using a `QueueClient` (or `TopicClient` if you are targetting a topic) or the `MessageSender`. -| In v4 | Equivalent in v7 | Sample | -|------------------------------------------------|------------------------------------------------------------------|--------| -| `MessageReceiver.ReceiveAsync()` | `ServiceBusReceiver.ReceiveMessageAsync()` | [Receive a message](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/servicebus/Azure.Messaging.ServiceBus/samples/Sample01_HelloWorld.md#sending-and-receiving-a-message) | -| `MessageReceiver.ReceiveAsync()` | `ServiceBusReceiver.ReceiveMessagesAsync()` | [Receive a batch of messages](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/servicebus/Azure.Messaging.ServiceBus/samples/Sample01_HelloWorld.md#sending-and-receiving-a-batch-of-messages) | -| `QueueClient.RegisterMessageHandler()` or `MessageReceiver.RegisterMessageHandler()` | `ServiceBusProcessor.ProcessMessageAsync += MessageHandler` `ServiceBusProcessor.ProcessErrorAsync += ErrorHandler` `ServiceBusProcessor.StartProcessingAsync()` | [Receive messages using processor](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/servicebus/Azure.Messaging.ServiceBus/samples/Sample04_Processor.md) | +While the `QueueClient` supported the simple send operation, the `MessageSender` supported that and advanced scenarios like scheduling to send messages at a later time and cancelling such scheduled messages. -### Working with sessions - -| In v4 | Equivalent in v7 | Sample | -|------------------------------------------------|------------------------------------------------------------------|--------| -| `MessageSender.SendAsync(new Message{SessionId = "sessionId"})` | `ServiceBusSender.SendMessageAsync(new ServiceBusMessage{SessionId = "sessionId"})` | [Send a message to session](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/servicebus/Azure.Messaging.ServiceBus/samples/Sample03_SendReceiveSessions.md) | -| `IMessageSession.ReceiveAsync()` | `ServiceBusSessionReceiver.ReceiveMessageAsync()` | [Receive a message from session](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/servicebus/Azure.Messaging.ServiceBus/samples/Sample03_SendReceiveSessions.md) | -| `IMessageSession.RegisterMessageHandler()` | `ServiceBusSessionProcessor.ProcessMessageAsync += MessageHandler` `ServiceBusSessionProcessor.ProcessErrorAsync += ErrorHandler` `ServiceBusSessionProcessor.StartProcessingAsync()` | [Receive messages from session processor](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/servicebus/Azure.Messaging.ServiceBus/samples/Sample05_SessionProcessor.md) | +```cs +// create a message to send +Message message = new Message(Encoding.Default.GetBytes("Hello world!")); -## Migration samples +// send using the QueueClient +QueueClient queueClient = new QueueClient(connectionString, queueName); +await queueClient.SendAsync(message); -### Sending and receiving a message +// send using the MessageSender +MessageSender sender = new MessageSender(connectionString, queueName); +await sender.SendAsync(message); +``` -In v4, `QueueClient`/`MessageSender`/`MessageReceiver` would be created directly, after which user would call `SendAsync()` method via `QueueClient`/`MessageSender` to send a message and `ReceiveAsync()` method via `MessageReceiver` to receive a message. +Now in `Azure.Messaging.ServiceBus`, we combine all the send related features under a common class `ServiceBusSender` that you can create from the top level client using the `CreateSender()` method. This method takes the queue or topic you want to target. This way, we give you a one stop shop for all your send related needs. -In v7, user would initialize the `ServiceBusClient` and call `CreateSender()` method to create a `ServiceBusSender` and `CreateReceiver()` method to create a `ServiceBusReceiver`. To send a message, user would call `SendMessageAsync()` via `ServiceBusSender` and to receive a message, user would call `ReceiveMessageAsync()` via `ServiceBusReceiver`. +We continue to support sending bytes in the message. Though, if you are working with strings, you can now create a message directly without having to convert it to bytes first. -In v4: +```cs +// create the client +var client = new ServiceBusClient(connectionString); -```csharp -string connectionString = ""; -string entityPath = ""; // create the sender -MessageSender sender = new MessageSender(connectionString, entityPath); +ServiceBusSender sender = client.CreateSender(queueName); -// create a message that we can send -Message message = new Message(Encoding.Default.GetBytes("Hello world!")); +// create a message to send +ServiceBusMessage message = new ServiceBusMessage("Hello world!"); -// send a message -await sender.SendAsync(message); +// send the message +await sender.SendMessageAsync(message); +``` -// create a receiver that we can use to receive the message -MessageReceiver receiver = new MessageReceiver(connectionString, entityPath); +The feature to send a list of messages in a single call was implemented by batching all the messages into a single AMQP message and sending that to the service. -// received a message -Message receivedMessage = await receiver.ReceiveAsync(); +While we continue to support this feature, it always had the potential to fail unexpectedly when the resulting batched AMQP message exceeded the size limit of the sender. To help with this, we now provide a safe way to batch multiple messages to be sent at once using the new `ServiceBusMessageBatch` class. -// get the message body as a string -string body = Encoding.Default.GetString(receivedMessage.Body.ToArray()); -Console.WriteLine(body); +In the below code snippet, `inputMessageArray` is an array of messages which we will loop over to safely batch and then send. -// Close the sender -await sender.CloseAsync(); +```cs +ServiceBusMessageBatch messageBatch = await sender.CreateMessageBatchAsync(); -// Close the receiver -await receiver.CloseAsync(); +for (var i = 0; i < inputMessageArray.Length; i++) +{ + if (!messageBatch.TryAddMessage(inputMessageArray[i])) + { + if (messageBatch.Count == 0) + { + Console.WriteLine($"Failed to fit message number in a batch {i}"); + break; + } + + // Decrement counter so that message number i can get another chance in a new batch + i--; + + // send the message batch and create a new batch + await sender.SendMessagesAsync(messageBatch); + messageBatch.Dispose(); + messageBatch = await sender.CreateMessageBatchAsync(); + } + else if (i == inputMessageArray.Length) + { + // send the final batch + await sender.SendMessagesAsync(messageBatch); + messageBatch.Dispose(); + } +} ``` -In v7: - -```C# Snippet:ServiceBusSendAndReceive -string connectionString = ""; -string queueName = ""; -// since ServiceBusClient implements IAsyncDisposable we create it with "await using" -await using var client = new ServiceBusClient(connectionString); - -// create the sender -ServiceBusSender sender = client.CreateSender(queueName); - -// create a message that we can send -ServiceBusMessage message = new ServiceBusMessage(Encoding.UTF8.GetBytes("Hello world!")); +### Receiving messages -// send the message -await sender.SendMessageAsync(message); +Previously, in `Microsoft.Azure.ServiceBus`, you could receive messages either by using a `QueueClient` (or `SubscriptionClient` if you are targetting a subscription) or the `MessageReceiver`. -// create a receiver that we can use to receive the message -ServiceBusReceiver receiver = client.CreateReceiver(queueName); +While the `QueueClient` supported the simple push model where you could register message and error handlers/callbacks, the `MessageReceiver` provided you with ways to receive messages (both normal and deferred) in batches, settle messages and renew locks. -// the received message is a different type as it contains some service set properties -ServiceBusReceivedMessage receivedMessage = await receiver.ReceiveMessageAsync(); -// get the message body as a string -string body = receivedMessage.Body.ToString(); -Console.WriteLine(body); -``` +```cs +// create the QueueClient +QueueClient queueClient = new QueueClient(connectionString, queueName); -### Sending and receiving a batch of messages +// define the message handler +async Task MessageHandler(Message message, CancellationToken token) +{ + // Process the message. + Console.WriteLine($"Received message with Body:{Encoding.UTF8.GetString(message.Body)}"); +} -In v4, `QueueClient`/`MessageSender`/`MessageReceiver` would be created directly, after which user would call `SendAsync()` method via `QueueClient`/`MessageSender` to send a batch of messages and `ReceiveAsync()` method via `MessageReceiver` to receive a batch of messages. +// define the error handler +Task ErrorHandler(ExceptionReceivedEventArgs exceptionReceivedEventArgs) +{ + Console.WriteLine($"Message handler encountered an exception {exceptionReceivedEventArgs.Exception}."); + return Task.CompletedTask; +} -In v7, user would initialize the `ServiceBusClient` and call `CreateSender()` method to create a `ServiceBusSender` and `CreateReceiver()` method to create a `ServiceBusReceiver`. There are two ways of sending several messages at once. +// Configure the message handler options with the error handler +var messageHandlerOptions = new MessageHandlerOptions(ErrorHandler); -The first way uses the `SendMessagesAsync`overload that accepts an IEnumerable of `ServiceBusMessage`. With this method, we will attempt to fit all of the supplied messages in a single message batch that we will send to the service. If the messages are too large to fit in a single batch, the operation will throw an exception. +// Register the message handler and options +queueClient.RegisterMessageHandler(MessageHandler, messageHandlerOptions); -The second way of doing this is using safe-batching. With safe-batching, you can create a `ServiceBusMessageBatch` object, which will allow you to attempt to add messages one at a time to the batch using the `TryAddMessage` method. If the message cannot fit in the batch, `TryAddMessage` will return false. If the `ServiceBusMessageBatch` accepts a message, user can be confident that it will not violate size constraints when calling `SendMessagesAsync()` via `ServiceBusSender`. To receive a set of messages, a user would call `ReceiveMessagesAsync()` method via `ServiceBusReceiver`. +// Or receive using the receiver +var receiver = new MessageReceiver(connectionString, queueName); +var receivedMessage = await receiver.ReceiveAsync(); +Console.WriteLine($"Received message with Body:{Encoding.UTF8.GetString(receivedMessage.Body)}"); +await receiver.CompleteAsync(receivedMessage); +``` -In v4: +Now in `Azure.Messaging.ServiceBus`, we introduce a dedicated class `ServiceBusProcessor` which takes your message and error handlers to provide you with the same simple way to get started with processing your messages as message handlers in the previous packages, with auto-complete and auto-lock renewal features. This class also provides a graceful shutdown via the `StopProcessingAsync` method which will ensure that no more messages will be received, but at the same time you can continue the processing and settling the messages already in flight. -```csharp -string connectionString = ""; -string entityPath = ""; -// create the sender -MessageSender sender = new MessageSender(connectionString, entityPath); +The concept of a receiver remains for users who need to have a more fine grained control over the receiving and settling messages. The difference is that this is now created from the top-level `ServiceBusClient` via the `CreateReceiver()` method that would take the queue or subscription you want to target. -// create a list of messages that we can send -var messagesToSend = new List(); +```cs +// create the ServiceBusClient +var client = new ServiceBusClient(connectionString); -for (var i = 0; i < 10; i++) +// define the message handler +async Task MessageHandler(ProcessMessageEventArgs args) { - Message message = new Message(Encoding.UTF8.GetBytes("Hello World" + i)); - messagesToSend.Add(message); + Console.WriteLine(args.Message.Body.ToString()); } -// send a list of messages -await sender.SendAsync(messagesToSend); - -// create a receiver that we can use to receive the messages -MessageReceiver receiver = new MessageReceiver(connectionString, entityPath); - -// received a list of messages -IList receivedMessages = await receiver.ReceiveAsync(maxMessageCount: 10); - -foreach (Message receivedMessage in receivedMessages) +// define the error handler +Task ErrorHandler(ProcessErrorEventArgs args) { - // get the message body as a string - string body = Encoding.Default.GetString(receivedMessage.Body.ToArray()); - Console.WriteLine(body); + Console.WriteLine($"Message handler encountered an exception {args.Exception}."); + return Task.CompletedTask; } -// Close the sender -await sender.CloseAsync(); +// create a processor and register handlers that we can use to process the messages +ServiceBusProcessor processor = client.CreateProcessor(queueName); +processor.ProcessMessageAsync += MessageHandler; +processor.ProcessErrorAsync += ErrorHandler; + +// start processing +await processor.StartProcessingAsync(); + +// Or receive using the receiver +var receiver = client.CreateReceiver(queueName); +var receivedMessage = await receiver.ReceiveMessageAsync(); +Console.WriteLine($"Received message with Body: {receivedMessage.Body}"); +await receiver.CompleteMessageAsync(receivedMessage); -// Close the receiver -await receiver.CloseAsync(); ``` +### Working with sessions -In v7: +Previously, in `Microsoft.Azure.ServiceBus`, you had the below options to receive messages from a session enabled queue/subscription +- Register message and error handlers using the `QueueClient.RegisterSessionHandler()` method to receive messages from an available set of sessions +- Use the `SessionClient.AcceptMessageSessionAsync()` method to get an instance of the `MessageSession` class that will be tied to a given sessionId or to the next available session if no sessionId is provided. -```C# Snippet:ServiceBusInitializeSend -string connectionString = ""; -string queueName = ""; -// since ServiceBusClient implements IAsyncDisposable we create it with "await using" -await using var client = new ServiceBusClient(connectionString); +While the first option is similar to what you would do in a non-session scenario, the second that allows you finer-grained control is very different from any other pattern used in the library. -// create the sender -ServiceBusSender sender = client.CreateSender(queueName); -IList messages = new List(); -messages.Add(new ServiceBusMessage(Encoding.UTF8.GetBytes("First"))); -messages.Add(new ServiceBusMessage(Encoding.UTF8.GetBytes("Second"))); -// send the messages -await sender.SendMessagesAsync(messages); -``` +Now in `Azure.Messaging.ServiceBus`, we simplfify this by giving session variants of the same methods and classes that are available when working with queues/subscriptions that do not have sessions enabled. -Or using the safe-batching feature: +The below code snippet shows you the session variation of the `ServiceBusProcessor`. -```C# Snippet:ServiceBusSendAndReceiveSafeBatch -ServiceBusMessageBatch messageBatch = await sender.CreateMessageBatchAsync(); -messageBatch.TryAddMessage(new ServiceBusMessage(Encoding.UTF8.GetBytes("First"))); -messageBatch.TryAddMessage(new ServiceBusMessage(Encoding.UTF8.GetBytes("Second"))); +```cs +// create a processor to receive events from the next available session +ServiceBusSessionProcessor processor = client.CreateSessionProcessor(queueName); -// send the message batch -await sender.SendMessagesAsync(messageBatch); -``` +// create a processor to receive events from the given set of sessions +var options = new ServiceBusSessionProcessorOptions +{ + SessionIds = ["my-session", "your-session"], +}; +ServiceBusSessionProcessor processor = client.CreateSessionProcessor(queueName, options); -And to receive a batch: +// create a processor to receive events from the 3 next available sessions +var options = new ServiceBusSessionProcessorOptions +{ + MaxConcurrentSessions = 3 +}; +ServiceBusSessionProcessor processor = client.CreateSessionProcessor(queueName); +``` -```C# Snippet:ServiceBusReceiveBatch -// create a receiver that we can use to receive the messages -ServiceBusReceiver receiver = client.CreateReceiver(queueName); +The below code snippet shows you the session variation of the receiver. Please note that creating a session receiver is an async operation because the library will need to get a lock on the session by connecting to the service first. -// the received message is a different type as it contains some service set properties -IReadOnlyList receivedMessages = await receiver.ReceiveMessagesAsync(maxMessages: 2); +```cs +// create a receiver to receive events from the next available session +ServiceBusSessionReceiver receiver = await client.CreateSessionReceiverAsync(queueName); -foreach (ServiceBusReceivedMessage receivedMessage in receivedMessages) +// create a receiver to receive events from the given session +var options = new ServiceBusSessionReceiverOptions { - // get the message body as a string - string body = receivedMessage.Body.ToString(); - Console.WriteLine(body); -} + SessionId = "my-session" +}; +ServiceBusSessionReceiver receiver = await client.CreateSessionReceiverAsync(queueName, options); ``` ## Additional samples