Azure / Azure/azure-functions-dotnet-worker
ServiceBusMessageActions not wrapped into Transaction
- Dominant language
- C#
- Stars
- 466
- Forks
- 215
- Avg merge
- 3d 10h
- Merged PRs (30d)
- 7
Description
### Description
I'm trying to do what sounds pretty simple in an Azure Function:
1. Receive a message from a Queue
2. Decide on a destination Queue based on the contents and configuration
3. Send the message to the destination Queue
I need this to all happen within a transaction to avoid either the message being lost or duplicated.
It appears this was pretty simple when the MessageReceiver was available, there is even a simple MS blog on the topic https://weblogs.asp.net/sfeldman/transactional-messaging-with-azure-functions-and-service-bus.
However I just cannot get this working using the more modern isolated worker approach. I would expect a simple code block such as below to not actually complete the message / send to the destination because the Complete() is commented out, but it appears the CompleteMessageAsync is processed immediately.
Is there any solution to this or are Service Bus transactions in Azure Functions no longer supported?
### Steps to reproduce
Sample code:
```
using System.Transactions;
using Azure.Messaging.ServiceBus;
using Azure.Messaging.ServiceBus.Administration;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Sample
{
public class Router(ILogger logger, IConfiguration configuration, ServiceBusClient serviceBusClient, ServiceBusAdministrationClient serviceBusAdministrationClient)
{
[Function(nameof(TenantRouter))]
public async Task Run(
[ServiceBusTrigger("%InputQueueName%", Connection = "ServiceBusEndpoint", AutoCompleteMessages = false)]
ServiceBusReceivedMessage message,
ServiceBusMessageActions messageActions,
CancellationToken cancellationToken)
{
// Code removed for simplicity
var targetQueue = "fromConfiguration";
if (!await serviceBusAdministrationClient.QueueExistsAsync(targetQueue, cancellationToken).ConfigureAwait(false))
{
_ = await serviceBusAdministrationClient.CreateQueueAsync(new CreateQueueOptions(targetQueue) { DeadLetteringOnMessageExpiration = true }, cancellationToken).ConfigureAwait(false);
}
var sender = serviceBusClient.CreateSender(targetQueue);
using var ts = new TransactionScope(TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Enabled);
await sender.SendMessageAsync(new ServiceBusMessage(message), cancellationToken).ConfigureAwait(false);
await messageActions.CompleteMessageAsync(message, cancellationToken);
// ts.Complete();
}
}
}
```
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.