Azure Function Bindings for Service Bus - azure

I'm following this documentation to create a trigger for a service bus queue.
I'd like to be able to access the message properties. I thought I could simply add Dictionary<string, object> properties to the parameter list like so:
public static void Run(
[ServiceBusTrigger(QueueName, Connection = "connectionSetting")]
// Message message,
string myQueueItem,
Int32 deliveryCount,
DateTime enqueuedTimeUtc,
string messageId,
string ContentType,
Dictionary<string,object> properties,
TraceWriter log)
But that throws:
Error indexing method 'Program.Run'. Microsoft.Azure.WebJobs.Host:
Cannot bind parameter 'properties' to type Dictionary`2. Make sure the
parameter Type is supported by the binding.
Here is a list of the possible parameter bindings. What am I getting wrong?
Update:
I tried changing the singature to
public static void Run(
[ServiceBusTrigger(QueueName, Connection = "connectionSetting")]
// Message message,
string myQueueItem,
Int32 deliveryCount,
DateTime enqueuedTimeUtc,
string messageId,
string ContentType,
IDictionary<string, object> properties,
TraceWriter log)
It produces the same error:
Error indexing method 'Program.Run'. Microsoft.Azure.WebJobs.Host:
Cannot bind parameter 'properties' to type IDictionary

For function v2 runtime, the name of the parameter has changed to UserProperties
To fix the error, update the parameter to the following:
IDictionary<string, object> UserProperties
Here is the related code from Service Bus extension.
https://github.com/Azure/azure-webjobs-sdk/blob/42a711763ddecca9df4caae9c7dc5fe16178880c/src/Microsoft.Azure.WebJobs.ServiceBus/Triggers/ServiceBusTriggerBinding.cs#L127

IDictionary<string, object> properties
Update:
for version 2 use the following bindings:
public static void Run(
[ServiceBusTrigger(QueueName, Connection = "connectionSetting")]
Message message,
string label,
Int32 deliveryCount,
DateTime enqueuedTimeUtc,
string messageId,
string ContentType,
ILogger log)
{
log.LogInformation($"C# ServiceBus queue trigger function processed message: {Encoding.UTF8.GetString(message.Body)}");
var userProperties = message.UserProperties;
}

Related

ServiceBus Trigger not binding CorrelationId

I have an Azure Function which is using a ServiceBus trigger with topics. I recently added correlationId to the list of bindable inputs however the correlationId is not binding. I have confirmed that the correlationId is set corectly on the inbound message. All tooling/NuGets are on the latest bits (as of today).
The function also happens to be a durable function as is obvious in the following code snippet
[FunctionName("TopicHandler")]
public static void Run([ServiceBusTrigger("%TopicName%", "SubscriptionName", Connection = "ServiceBusConnection")]string messageBody,
string label,
string messageId,
string correlationId,
IDictionary<string, object> userProperties,
[OrchestrationClient] DurableOrchestrationClient starter,
ExecutionContext executionContext,
ILogger logger)
{
....
Documetnation seems to imply this should work:
https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-service-bus#trigger---message-metadata
However this is not completely explict as to whether it only works with queues or not.
https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-messages-payloads#message-routing-and-correlation
Any guidance would be appreciated before I create a custom property.

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

Azure Function Blob Trigger - Container Name as Setting

I'm trying to create an Azure function that connects to a container that is not hard-coded, much like my connection.
public static void Run(
[BlobTrigger("Container-Name/ftp/{name}", Connection = "AzureWebJobsStorage")]Stream blobStream,
string name,
IDictionary<string, string> metaData,
TraceWriter log)
The connection property is able to get the connection value directly from local.settings.json. It does not appear that capability is an option for "container-name", or if so, not with the appended /ftp/{name}.
So, is there a way to set the container name based on settings for an Azure Function Blob Trigger?
You can define your function like this:
public static void Run(
[BlobTrigger("%mycontainername%/ftp/{name}", Connection = "AzureWebJobsStorage")]
Stream blobStream,
string name,
IDictionary<string, string> metaData,
TraceWriter log)
and then define an application setting called mycontainername to contain the actual container name.

How to get deviceid of message in Azure function that is triggered by IOThub message

I have an Azure function that is triggered by IOThub. So in the Azure function, I have
public static async Task Run(EventData myIoTHubMessage1, TraceWriter log)
How do I get the device id from the Event Data.
I tried
log.Info("devid="+myIoTHubMessage1.SystemProperties["ConnectionDeviceId"]);
It gave an error saying
The given key was not present in the dictionary.
the following document says that
https://learn.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-messages-construct
ConnectionDeviceId contains the deviceId. Would anybody know how to retrieve the deviceid from EventData or should I use some other class.
You can get device ID from SystemProperties:
public static async Task Run(EventData myIoTHubMessage1, TraceWriter log)
{
var deviceId = myIoTHubMessage1.SystemProperties["iothub-connection-device-id"];
// ....
}
for (EventData receivedEvent : receivedEvents) {
String deviceId = (String) receivedEvent.getProperties().get("deviceId");
log.info("From:" + deviceId);
}

Is Attribute Routing possible in Azure Functions

I am trying to enforce a route parameter to be guid but getting below error
"Exception while executing function: GetUser -> One or more errors
occurred. -> Exception binding parameter 'req' -> Invalid cast from
'System.String' to 'System.Guid'."
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Admin, "get", Route = "GetUser/{userId:guid}")] HttpRequestMessage req,
Guid userId, ILogger log)
{
}
The request i am making is http://localhost:7071/api/GetUser/246fb962-604d-4699-9443-fa3fa840e9eb/
Am i missing some thing? Cannot we enforce route parameter to be guid ?
Invalid cast from 'System.String' to 'System.Guid'
I can reproduce same issue when use Route constraint {userId:guid} in Azure httptrigger function on my side, you can try to open an issue to give a feedback.
Besides, if possible, you can try to call Guid.TryParse method to convert the string back to Guid value in function code, the following code is for your reference.
public static string Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = "GetUser/{userId:guid}")]HttpRequestMessage req, string userId, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
Guid newGuid;
var resmes = "";
if (Guid.TryParse(userId, out newGuid))
{
resmes = "userid: " + newGuid;
}
else {
resmes = "error";
}
return resmes;
}

Resources