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
likeOkObjectResult
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.

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.

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.

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.

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

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.

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…

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

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.

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

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

Finally, I find my queues and I browse them.

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.

Open a browser and insert the url for the function, in my case is
https://localhost:7071/api/Function1
In the window, I see the function sent some output without errors.

So, in Service Bus Explorer, I refreshed the queue with F5. Now I see the 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.
[…] one Azure Function at the same time but each of them on a different port because by default all Azure Functions are starting on port 7071. How can I do […]