Azure Functions Featured Image

In this post I like to analyse how to return multiple output in Azure Functions with C# and Service Bus. If you want more info, in the last week or so, I published some posts about Azure Function in C# or F# like “Create Azure Function in C# Script and Service Bus” or “Creating Azure Function in F#“.

You have a platform on Azure and two different services are triggered by a message from Service Bus. At some point, you have an Azure Function doing a procedure that has to do 3 outputs:

  • response an ActionResult like OkObjectResult to allow the UI to continue its activity
  • send a message to a Service Bus to trigger another process
  • and also send another message to another Service Bus to trigger a different process

In the Microsoft documentation, Azure Service Bus output binding for Azure Functions, there is only one output, usually to Service Bus and the code Microsoft offers is pretty easy:

[FunctionName("ServiceBusOutput")]
[return: ServiceBus("myqueue", Connection = "ServiceBusConnection")]
public static string ServiceBusOutput([HttpTrigger] dynamic input, ILogger log)
{
    log.LogInformation($"C# function processed: {input.Text}");
    return input.Text;
}

This function is triggered from an HttpRequest and return (see the annotation return:) a message in Service Bus with input.Text. This is the base function I will use.

Setup a Service Bus

In the Azure Portal, I’m going to create a new Service Bus. I’m calling it aztest.

Create Service Bus in Azure Portal
Create Service Bus in Azure Portal

After that, I create two new queues: message-out1 and message-out2. Click on + Queue in the portal and type the name of the queue. Then press Create to create each queue.

Create a new queue in Azure Service Bus
Create a new queue in Azure Service Bus

Later in the project I need to connection string to this Service Bus: in the menu on the left, select Shared access policies, then click on RootManageSharedAccessKey and save the Primary Connection String.

Copy the connection string for the Service Bus
Copy the connection string for the Service Bus

For now, I’m done with Service Bus and the Azure Portal.

Create a new project with Visual Studio

Open Visual Studio and Create a new project.From the list, select Azure Functions and press Next.

Create a new project with Visual Studio
Create a new project with Visual Studio

Configure your new project: I decide to call it AFMultiOutput (Azure Functions Multiple Output). Press on Create.

Configure your new project
Configure your new project

In the next window of the creating wizard, I choose Azure Functions v3 (.NET Core) and Http Trigger as trigger of the function. Then press Create.

Create a new Azure Functions Application

Done! Now, I have to add a package to connect Service Bus. Right click on the project in the Solution Explorer and click on Manage NuGet Packages…

Add NuGet for connecting Service Bus
Add NuGet for connecting Service Bus

Now, search and install Microsoft.Azure.WebJobs.Extensions.ServiceBus

NuGet packages to install Service Bus
NuGet packages to install Service Bus

I have to add the connection strings for Service Bus. For that, in the local.settings.json I add two new keys called ServiceBusConnectionString1 and ServiceBusConnectionString1. Replace <connection1> and <connection2> with the Primary Connection String from the Azure Portal. Be careful with the name of those keys.

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet",
    "ServiceBusConnectionString1": "<connection1>",
    "ServiceBusConnectionString2": "<connection2>"
  }
}

Next step is to add in the signature of the function our output. As I said, I want two outputs, one for each Service Bus. The new signature is that:

public async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
    [ServiceBus("message-out1", Connection = "ServiceBusConnectionString1")] 
    IAsyncCollector<dynamic> outputServiceBus1,
    [ServiceBus("message-out2", Connection = "ServiceBusConnectionString2")] 
    IAsyncCollector<dynamic> outputServiceBus2,
    ILogger log)

Automatically, the Azure Function will read the settings to grab the connection strings. Notice the first parameter after ServiceBus is the name of the queue.

To write multiple values to an output binding, or if a successful function invocation might not result in anything to pass to the output binding, use the ICollector or IAsyncCollector types. These types are write-only collections that are written to the output binding when the method completes.

As outputs of this function, I use those variables and add a simple message in each queue.

await outputServiceBus1.AddAsync("Item1");
await outputServiceBus2.AddAsync("Item2");

Finally, I have to return an HttpCode to the caller. It is easy to reply with an object, in this case a null object.

return new OkObjectResult(null);

The multiple output in Azure Functions with C# is done and looks like that:

public class Function1
{
    [FunctionName("Function1")]
    public async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
        [ServiceBus("message-out1", Connection = "ServiceBusConnectionString1")] 
        IAsyncCollector<dynamic> outputServiceBus1,
        [ServiceBus("message-out2", Connection = "ServiceBusConnectionString2")] 
        IAsyncCollector<dynamic> outputServiceBus2,
        ILogger log)
    {
        await outputServiceBus1.AddAsync("Item1");
        await outputServiceBus2.AddAsync("Item2");

        return new OkObjectResult(null);
    }
}

Explore your Service Bus

To explore this brand-new Service Bus, I download Service Bus Explorer from Github, a really good project. If you don’t want to source code, the installer is here.

Service Bus Explorer
Service Bus Explorer

After that, I have to connect Service Bus Explorer with the Service Bus. For the Service Bus page, on the left, there is an option called “Shared access policies“: click there and click on “RootManageSharedAccessKey“. Now, copy the “Primary Connection String“ or copy the keys from your local.settings.json

Copy the connection string for the Service Bus
Copy the connection string for the Service Bus

Then, in Service Bus Explore, click on File > Connect. Paste the “Primary Connection String” in the connection string textbox and then press Ok.

Service Bus Explorer: connect
Service Bus Explorer: connect

Finally, I find my queues and I browse them.

Service Bus Explorer with my queues
Service Bus Explorer with my queues

Run the function

It is time to run the function and verify if it is working as I want. Run the project in Visual Studio.

Azure Function is running
Azure Function is running

Open a browser and insert the url for the function, in my case is

http://localhost:7071/api/Function1

In the window, I see the function sent some output without errors.

The function replied to my request without errors
The function replied to my request without errors

So, in Service Bus Explorer, I refreshed the queue with F5. Now I see the messages in both queues.

Messages in both queues
Messages in both queues

ICollector<T> vs IAsyncCollector<T>

So, what is the difference between ICollector<T> and IAsyncCollector<T>?

ICollector<T>.Add(item) will always perform the add operation against the underlying service immediately. E.g. the implementation for the Queue binding will enqueue messages immediately as they are added.

IAsyncCollector<T>.AddAsync(item) behavior varies from binding to binding, based on whether the underlying service supports batching. In such cases, AddAsync may only actually save the added items to be flushed later by the corresponding IAsyncCollector<T>.FlushAsync method. When a function completes successfully FlushAsync will be called automatically. You can allow the auto-flush behavior to flush for you, or you may choose to call FlushAsync manually in your function as you need to.

Conclusion

Finally, I have my multiple output in Azure Functions with C# and Service Bus. Not really difficult but a bit long. If you have any questions, please use our forum.

If you want to take a look at my code, visit my repository on Github.

By Enrico

My greatest passion is technology. I am interested in multiple fields and I have a lot of experience in software design and development. I started professional development when I was 6 years. Today I am a strong full-stack .NET developer (C#, Xamarin, Azure)

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: