I have a queue trigger (for example):
[FunctionName("Handle-Invoice")]
[StorageAccount("StorageAccountConnectionString")]
public async Task InvoiceQueueTrigger(
[QueueTrigger("invoice-queue")] CloudQueueMessage cloudMessage,
ExecutionContext context,
ILogger log)
{
// Async code which might need access to the storage account for the queue
}
Where StorageAccountConnectionString is defined in the function's config.
I'm wondering if there's a way to implicitly access the value of the connection string defined in the StorageAccount attribute?
I know I can access the value directly from either the environment or configuration but it'd be good to access the value either by a binding or some other method.
Thanks.
Related
I am following on a possible upgrade of azure function runtime from v3 to v4 with dotnet. While doing so, I am testing out the isolated option for the project. However I am unable to get message metadata such as DequeueCount, MessageId etc in the queue trigger.
Previously with in-process option, I used to bind CloudQueueMessage but that doesn't seem to work in the isolated mode. Doing so, throws and error -
Cannot convert input parameter 'myQueueItem' to type 'Microsoft.WindowsAzure.Storage.Queue.CloudQueueMessage' from type 'System.String'
This was my isolated queue function binding
[Function("TestApp")]
public void Run([QueueTrigger("sample-queue", Connection = "")] CloudQueueMessage myQueueItem, FunctionContext context)
After looking for a while, I think here it says that, in isolated process we can only bind string. Simple JSON - Object also works.
Is there any way to get these message metadata (members of the CloudQueueMessage) in the isolated azure function?
Thanks.
For DequeueCount property I use this:
[Function("MyQueueTrigger")]
public void Run([QueueTrigger("my-queue-name")] string myQueueItem, int dequeueCount)
{
_logger.LogInformation("Queue item: {item}. Dequeue count: {dequeueCount}.", myQueueItem, dequeueCount);
}
I am running a v4 Azure Function in an isolated process. It is triggered by a message coming from a Service Bus queue. I have created a simple middleware and would like to get my hands on the incoming data (a simple string). How can I do that from the middleware itself? It doesn't seem FunctionContext is of use in this case.
public class SimpleMiddleware : IFunctionsWorkerMiddleware
{
public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
{
await next(context);
}
}
The service bus message data and metadata is extracted and placed into the BindingData dictionary. Try looking at context.BindingContext.BindingData and find the key that exposes your message's data.
Is there now a way to set the Trigger Properties(Name/Connection) using the value from Azure App Configuration?.
I added a startup class that reads the data from Azure App Configuration but it seems the trigger set its properties earlier than that, therefore not able to bind the data that came from the app configuration.
I also found this thread about it but im not sure if there is a new update?:
https://github.com/MicrosoftDocs/azure-docs/issues/63419
https://github.com/Azure/AppConfiguration/issues/203
You can do this. The following code gets the name of the queue to monitor from an app setting, and it gets the queue message creation time in the insertionTime parameter:
public static class BindingExpressionsExample
{
[FunctionName("LogQueueMessage")]
public static void Run(
[QueueTrigger("%queueappsetting%")] string myQueueItem,
DateTimeOffset insertionTime,
ILogger log)
{
log.LogInformation($"Message content: {myQueueItem}");
log.LogInformation($"Created at: {insertionTime}");
}
}
Similarly, you can use this approach for other triggers.
Imagine that I have a storage account with a blob container, which get files uploaded eventually.
I want to process each file that reaches on the blob storage, open it, extract and store information. Definitively a expensive operation that could fit in a Durable Functions scenario.
Here's the trigger:
[FunctionName("PayrollFileTrigger")]
public static async Task Start(
[BlobTrigger("files/{name}", Connection = "AzureWebJobsStorage")]Stream myBlob, string name,
[DurableClient] IDurableOrchestrationClient starter,
ILogger log)
{
string instanceId = await starter.StartNewAsync("PayrollFile_StartFunction", "payroll_file", name);
}
...which calls the orchestration:
[FunctionName("PayrollFile_StartFunction")]
public async static Task<IActionResult> Run(
[OrchestrationTrigger] IDurableOrchestrationContext context, string blobName,
ExecutionContext executionContext, ILogger log)
{
//Downloads the blob
string filePath =
await context.CallActivityWithRetryAsync<string>("DownloadPayrollBlob", options, blobName);
if (filePath == null) return ErrorResult(ERROR_MSG_1, log);
//Extract data
var payroll =
await context.CallActivityWithRetryAsync<Payroll>("ExtractBlobData", options, filePath);
... and so on (just a sample here) ...
}
But there is a problem. While testing this error occurs, meaning, I think, that I can't start another orchestration with the same id:
An Orchestration instance with the status Pending already exists.
1 - So if I push many files to the container which the trigger is "listening", in a short period of time, the orchestration will get busy with one of them and will ignore other further events?
2 - When the orchestration will get rid of pending status? It occurs automatically?
3 - Should I create a new orchestration instance for each file to be processed? I know you can omit the instanceId parameter, so it get generated randomly and never conflicts with one already started. But, is it safe to do? How do I manage them and ensure they will get finished sometime?
string instanceId = await starter.StartNewAsync("PayrollFile_StartFunction", "payroll_file", name);
The second argument is the instanceId, which is required to be unique.
Instead, try:
string instanceId = await starter.StartNewAsync("PayrollFile_StartFunction", input: name);
Depending on what you want you might want to have only 1 durable instance per file. Microsoft state that you should
Use a random identifier for the instance ID. Random instance IDs help ensure an equal load distribution when you're scaling orchestrator functions across multiple VMs. The proper time to use non-random instance IDs is when the ID must come from an external source, or when you're implementing the singleton orchestrator pattern.
https://learn.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-instance-management?tabs=csharp#start-instances
In your specific case I'd say you can go without supplying the instanceId yourself and perhaps log the generated instanceId or write it in a storage solution alongside information about the file that started the orchestration.
I'm trying to bind to MessageReceiver in an Azure Service Bus Triggered Function.
My goal is to handle dead letter queue messages and complete them.
public static class Function1
{
[FunctionName("Function1")]
public static async Task Run(
[ServiceBusTrigger(
"<topicName>",
"<subscriptionName>/$DeadLetterQueue",
Connection = "connectionstring")]
Message message,
ILogger logger,
MessageReceiver messageReceiver)
{
// TODO: Perform some actions
await messageReceiver.CompleteAsync(message.SystemProperties.LockToken);
}
The problem is that it fails to bind to the MessageReceiver class.
Microsoft.Azure.WebJobs.Host: Error indexing method 'Function1'. Microsoft.Azure.WebJobs.Host: Cannot bind parameter 'receiver' to type MessageReceiver. 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.).
Any ideas why the binding fails?
I figured out what was wrong. I was using 'receiver' as parameter name for MessageReceiver. It turned out that the parameter name has to be 'messageReceiver'. The example I was looking at first used 'receiver', so is this maybe something that has changed?