Azure Function V2 Service Bus Message Deferral - c#

I am attempting to convert my v1 function to a v2 function, but I cannot find a replacement for deferring a message.
In V1 of Azure Functions it was a method on the BrokeredMesage called .DeferAsync(). In V2 there is no longer a BrokeredMessage but just a Microsoft.Azure.ServiceBus.Message and this does not contain the method of .DeferAsync().
According to the docs:
The API is BrokeredMessage.Defer or BrokeredMessage.DeferAsync in the .NET Framework client, MessageReceiver.DeferAsync in the .NET Standard client, and mesageReceiver.defer or messageReceiver.deferSync in the Java client.
But how can I get access to the MessageReciever?
Here is an example of my function:
[FunctionName("MyFunction")]
public static void Run([ServiceBusTrigger("topic", "subscription", Connection = "AzureServiceBusPrimary")]Message message, ILogger log)
{
//Code
}
So does anyone know how to defer a V2 Message that is triggered from the Azure Service Bus?

As you mention, the new message receiver offers an async defer method and you can add this to your function by using the following code:
[FunctionName("MyFunction")]
public static async Task Run([ServiceBusTrigger("topic", "subscription", Connection = "AzureServiceBusPrimary")]Message message, string lockToken, MessageReceiver messageReceiver, ILogger log)
{
//Your function logic
await messageReceiver.DeferAsync(lockToken);
}

Related

Is it possible that 2 azure functions can get triggered by one eventhub?

Is it possible that 2 azure functions can get triggered by one eventhub? One azure function will write its data to database1 and the other azure function writes its data to database2
[FunctionName("EventToDB1")]
public async System.Threading.Tasks.Task Run([EventHubTrigger("eventhub", Connection = "Debezium")]
EventData[] events, ILogger log)
{
{
[FunctionName("EventToDB2")]
public async System.Threading.Tasks.Task Run([EventHubTrigger("eventhub", Connection = "Debezium")]
EventData[] events, ILogger log)
{
{
answer on the possibility of having 2 azure functions get triggered by one eventhub
Yes that is possible by using different consumer groups. Because you specified the same connection to the Event Hub, being "Debezium", I Assume you want both funtions to process the same message. You have to create a new consumer group and specify the name using the ConsumerGroup property of the EventHubTrigger attribute (The default consumergroup is $Default):
public class EventToDB1
{
[FunctionName("EventToDB1")]
public async System.Threading.Tasks.Task Run(
[EventHubTrigger("eventhub",
Connection = "Debezium",
ConsumerGroup = "CG1")]
EventData[] events, ILogger log)
{
}
}
public class EventToDB2
{
[FunctionName("EventToDB2")]
public async System.Threading.Tasks.Task Run(
[EventHubTrigger("eventhub",
Connection = "Debezium",
ConsumerGroup = "CG2")]
EventData[] events, ILogger log)
{
}
}
Each consumer group receives the same messages from the Event Hub.
I do agree with #peter bons, you need to create two consumer groups for that and you can create two consumers by below process:
You can also use logic apps to work with event hubs.

Cannot bind parameter 'message' to type ServiceBusReceivedMessage

I have Azure WebJobs methods registered as below
Program.cs
hostBuilder.ConfigureWebJobs(webJobsBuilder =>
{
webJobsBuilder
.AddTimers()
.AddAzureStorageCoreServices()
.AddAzureStorageQueues()
.AddAzureStorageBlobs()
.AddServiceBus();
});
UserFunctions.cs
[FunctionName("SendRegisteredUserVerificationEmailsServiceBusTrigger")]
public async Task SendRegisteredUserVerificationEmailsServiceBusTrigger([ServiceBusTrigger(BookStoreServiceBusQueueNames.RegisteredUsers, Connection = "AzureWebJobsServiceBusConnectionString", IsSessionsEnabled = false)] Int32 deliveryCount,
DateTime enqueuedTimeUtc, ServiceBusReceivedMessage message, ServiceBusMessageActions messageActions)
{
...
}
I checked the code samples below:
https://learn.microsoft.com/en-us/dotnet/api/overview/azure/microsoft.azure.webjobs.extensions.servicebus-readme-pre
And could not find a difference.
I am using following nuget packages:
https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.ServiceBus/
https://www.nuget.org/packages/Azure.Messaging.ServiceBus/
I am getting following error:
InvalidOperationException: Cannot bind parameter 'message' to type
ServiceBusReceivedMessage. Make sure the parameter Type is supported by the binding.
If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make
sure you've called the registration method for the extension(s) in your startup code (e.g.
builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).
Am I missing something here?
though Its too late. still I would like to answer this so it will be helpful in future. Today, I faced the same error and I tried to compare the changes. one thing I notice was we shouldn't have the parameter to get the message properties such as enqueuedtime, delivery count, message body, etc. instead these are already available in ServiceBusReceivedMessage message. so below piece throws error at runtime, with the parameter
[FunctionName("Function2")]
public void Run([ServiceBusTrigger("testtopic", "testsub", Connection = "sbconnectionstring", AutoCompleteMessages = false)] string messageBody,
ServiceBusReceivedMessage message, ServiceBusMessageActions messageActions)
{
Console.WriteLine($"C# ServiceBus topic trigger function processed message: {messageBody}");
}
and this one works fine. without properties param
[FunctionName("Function2")]
public void Run([ServiceBusTrigger("testtopic", "testsub", Connection = "sbconnectionstring", AutoCompleteMessages = false)]
ServiceBusReceivedMessage message, ServiceBusMessageActions messageActions)
{
Console.WriteLine($"C# ServiceBus topic trigger function processed message: {message.Body}");
messageActions.CompleteMessageAsync(message);
}
It seems there's a naming issue. Binding message parameter to type ServiceBusReceivedMessage seems problematic.
Per-message triggers
To run a function every time a message is sent to a Service Bus queue or subscription apply the ServiceBusTrigger attribute to a string, byte[], or ServiceBusReceivedMessage parameter.
[FunctionName("TriggerSingle")]
public static void Run(
[ServiceBusTrigger("<queue_name>", Connection = "<connection_name>")] string messageBodyAsString,
ILogger logger)
{
logger.LogInformation($"C# function triggered to process a message: {messageBodyAsString}");
}
parameter named as messageBodyAsString
Batch triggers
To run a function for a batch of received messages apply the ServiceBusTrigger attribute to a string[], or ServiceBusReceivedMessage[] parameter.
[FunctionName("TriggerBatch")]
public static void Run(
[ServiceBusTrigger("<queue_name>", Connection = "<connection_name>")] ServiceBusReceivedMessage[] messages,
ILogger logger)
{
foreach (ServiceBusReceivedMessage message in messages)
{
logger.LogInformation($"C# function triggered to process a message: {message.Body}");
logger.LogInformation($"EnqueuedTime={message.EnqueuedTime}");
}
}
parameter named as messages
Docs link: https://learn.microsoft.com/en-us/dotnet/api/overview/azure/microsoft.azure.webjobs.extensions.servicebus-readme-pre

Azure Function Event Hub Output Binding not working when deployed

I am using an Azure Function to get messages from a Rabbit MQ Broker to an Event Hub.
The function works perfect when I run it locally.
Here is the code of the function:
using System.Text;
using System.Dynamic;
using System.Threading.Tasks;
using CaseOnline.Azure.WebJobs.Extensions.Mqtt;
using CaseOnline.Azure.WebJobs.Extensions.Mqtt.Messaging;
using Microsoft.Azure.WebJobs;
using Newtonsoft.Json;
public static class Test
{
[FunctionName("EventHubOutput")]
public static async Task Run(
[MqttTrigger("topic/#")] IMqttMessage message,
[EventHub("eventhubname", Connection = "EventHubConnectionAppSetting")] IAsyncCollector<string> outputEvents,
ILogger log)
{
var body = message.GetMessage();
var bodyString = Encoding.UTF8.GetString(body);
dynamic obj = JsonConvert.DeserializeObject<ExpandoObject>(bodyString);
obj.Topic = message.Topic;
await outputEvents.AddAsync(JsonConvert.SerializeObject(obj));
}
}
When deployed and run in the Azure portal, I get the following error messages:
Microsoft.Azure.WebJobs.Host.FunctionInvocationException: Exception while executing function: EventHubOutput
---> System.InvalidOperationException: Error while handling parameter outputEvents after function returned:
---> System.Net.Sockets.SocketException (0xFFFDFFFF): Name or service not known
at (...)
Any idea what the problem might be?
Thank you.
You are using the bindings incorrectly. Check out RabbitMQ bindings for Azure Functions overview.
The following example shows a C# function that reads and logs the RabbitMQ message as a RabbitMQ Event:
[FunctionName("RabbitMQTriggerCSharp")]
public static void RabbitMQTrigger_BasicDeliverEventArgs(
[RabbitMQTrigger("queue", ConnectionStringSetting = "rabbitMQConnectionAppSetting")] BasicDeliverEventArgs args,
ILogger logger
)
{
logger.LogInformation($"C# RabbitMQ queue trigger function processed message: {Encoding.UTF8.GetString(args.Body)}");
}
The following example shows how to read the message as a POCO:
namespace Company.Function
{
public class TestClass
{
public string x { get; set; }
}
public class RabbitMQTriggerCSharp{
[FunctionName("RabbitMQTriggerCSharp")]
public static void RabbitMQTrigger_BasicDeliverEventArgs(
[RabbitMQTrigger("queue", ConnectionStringSetting = "rabbitMQConnectionAppSetting")] TestClass pocObj,
ILogger logger
)
{
logger.LogInformation($"C# RabbitMQ queue trigger function processed message: {pocObj}");
}
}
}
I recommend you to check out this complete guide to setup Rabbit MQ Trigger in Azure Functions: RabbitMQ trigger for Azure Functions overview

Azure Function .Net 5 Using ServiceBusTrigger

I'm using .net 5 azure function with ServiceBusTrigger. In previous versin. I was using a parametr MessageReceiver to tell azure that I compleated task. For that I used
await messageReceiver.CompleteAsync(message.SystemProperties.LockToken);
My problem is that i new version (.net5) unlike the previous version (.net core 3.1) is param in string (json) not in object (maybe it's the error but I don't know which object to use, object I tried was Microsoft.Azure.ServiceBus.Core.MessageReceiver).
But now i do not have the object so i can not call the method.
Does someone knows the answer? Thak you for your time.
Example of my code:
Function("TasksFunction")]
public async Task Run([ServiceBusTrigger("queue", Connection = "ConnectionString")] string message, string id, MessageReceiver messageReceiver, FunctionContext executionContext)
{
try
{
await messageReceiver.CompleteAsync(message.SystemProperties.LockToken);
await DoWorkAsync(messageReceiver, message);
}
catch (Exception e)
{
log.LogCritical(e);
}
}
Try to bind your message to Message type instead of string
public async Task Run([ServiceBusTrigger("queue", Connection = "ConnectionString")] Message message, string id, MessageReceiver messageReceiver, FunctionContext executionContext)
Message has property SystemPropertiesCollection and use message.SystemProperties.LockToken

Test Brokered Message

Now i'm working at writing unit test on azure service bus trigger function
It's highly needed to mock somehow BrokeredMessage object that pass around into function. Function declaration is given below:
public static void Run(
[ServiceBusTrigger("saas01.queue.dbmigration", AccessRights.Manage, Connection = "connection")]BrokeredMessage message)
Unfortunately, i can't find any applicable way to mock it. It hardly mocking du to this class is sealed and i can't event create wrapper around it. Do you have some ideas about it?
Thanks for helping
,
One solution is to create a wrapper around BrokeredMessage you can test, as is done here. Here's also a MSDN post to the ServiceBus team that talks about using a wrapper too.
Note that Azure Functions V2 uses the Message class, which is public and not sealed.
[FunctionName("ServiceBusFunc")]
public static void Run([ServiceBusTrigger("myqueue", AccessRights.Manage, Connection = "ServiceBus")]BrokeredMessage myQueueItem, TraceWriter log)
{
var message = new MyBrokeredMessage(myQueueItem);
BusinessLogic(message, log);
}
public static void BusinessLogic(MyBrokeredMessage myMessage, TraceWriter log)
{
var stream = myMessage.GetBody<Stream>();
var reader = new StreamReader(stream);
log.Info($"C# ServiceBus queue trigger function processed message: '{reader.ReadToEnd() }'");
}
public class MyBrokeredMessage
{
private BrokeredMessage _msg;
public MyBrokeredMessage(BrokeredMessage msg) => _msg = msg;
public T GetBody<T>()
{
return _msg.GetBody<T>();
}
}

Categories