On my Twitch stream today, I wanted to use Azure Functions to receive notifications when a stream goes offline and start downloading the chat transcript. I’m writing this post to share the things I learned while writing that code so that you can benefit from my research as I bumped and bruised my way through the code
Configuration of a QueueTrigger
The QueueTrigger is a powerful feature in an Azure Function to allow you to execute some code when data arrives in an Azure Queue. My idea: let’s configure a function to listen for an entry on a queue that indicates the Twitch channel to monitor. We would kick off a webhook subscription request for Twitch to send us notice whenever a live stream ended. The syntax of using a QueueTrigger comes in the form of a hint as a parameter to a C# function:
public async Task DequeueMe(
[QueueTrigger("myQueueName", Connection = "AzureStorageConnectionString")] CloudQueueMessage msg,
ILogger logger)
{
logger.LogTrace($"Message contents: {msg.AsString}");
logger.LogDebug("Completed");
}
The documentation for the QueueTrigger talks about bindings and function.json and host.json files and connection strings. In our scenario using .NET Core in a class library, these are over-complications. Let’s break it down:
- The first argument is the name of the Azure Queue to monitor
- The second argument is the name of the configuration setting that contains the ConnectionString for the storage account that holds the Azure Queue you are targeting.
So… where do you store the ConnectionString? Well… that’s not exactly a simple answer. On Azure, you store the value in the Application Settings for the Functions application, under Configuration. When developing locally on your machine, place the value in local.settings.json inside of the “Values” section:
{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", "FUNCTIONS_WORKER_RUNTIME": "dotnet", "AzureStorageConnectionString": "UseDevelopmentStorage=true" } }
In the above sample code, we are using the Azure Storage Emulator locally. This allows us to work with a local version of data storage that behaves identically to the online Azure managed resources. The connection string for the emulator is this ‘UseDevelopmentStorage=true’ syntax.
Accessing Configuration Values
What if you’d like to access the other configuration values of the application? The configuration values, regardless of developing locally or on Azure, are exposed as environment variables. You COULD read the environment variables directly, or you could add a constructor to your function and receive the IConfiguration object just as you would in a .NET Core applications
public MyFunctionClass(IConfiguration configuration) {
_Configuration = configuration;
}
You can then access your Configuration as you normally would in the class hosting your functions. This works really well if you also make your methods instance methods and not static methods.
Troubleshooting
In my case, I ran into a strange scenario where the following happened on the local console:
Here is where I ran into confusion. In my case, shown above, 1 job function was ‘identified’ but 2 functions were listed. The one with the QueueTrigger, ‘StreamManagement.Subscribe’ executes and returns this “System.Collections.Immutable: This operation cannot be performed on a default instance of ImmutableArray.” error. My StreamManagement.Subscribe method clearly is triggered due to the presence of an entry in my connected queue, but I am unable to debug into the method. What’s the story there?
After some inspection, I was able to deduce that the FunctionName attribute was missing from the Subscribe method. This error was a bit misleading, and I’m blogging it here in case you run into the same error.
Morale of the story: ALWAYS use a FunctionName in your Azure Functions.