Using Triggers & Bindings in Azure Functions V2
By rickvdbosch
- 3 minutes read - 550 wordsTo start things off: yes. There’s some pretty decent documentation on all the available Triggers & Bindings in Azure Functions. Like this overview page. And you can also find documentation on specific bindings, like the Service Bus Binding. Nevertheless, I wanted to add to that documentation with some simple, real-world examples using triggers & bindings. So here goes.
Writing to Blob Storage
Consider this very simple BlobTriggered Azure Function. It copies the triggering file into a different container in Azure Blob Storage:
public static async Task Run(
[BlobTrigger("upload/{name}", Connection = "scs")]Stream addedBlob,
string name, ILogger log)
{
// Setting up my BlobStorageRepository (it's a wrapper)
var connectionString = Environment.GetEnvironmentVariable("scs", EnvironmentVariableTarget.Process);
var blobStorageRepository = new BlobStorageRepository(connectionString);
await blobStorageRepository.AddFileAsync("copied", name, addedBlob);
}
This looks like a decent solution. But bindings can make this code so much simpler!
Consider the next code:
public static async Task Run(
[BlobTrigger("upload/{name}", Connection = "scs")]Stream addedBlob,
[Blob("copied/{name}", FileAccess.Write, Connection = "scs")]Stream stream,
string name, ILogger log)
{
await addedBlob.CopyToAsync(stream);
}
The result here is the same! By using the out binding, you can directly access the stream in the “copied” container. And you can use the “name” parameter from the input binding.
Next to that, it’s not just Stream you can bind to. For the out bindings, here’s the list of types you can bind to (taken from this article):
- TextWriter
- out string
- out byte[]
- CloudBlobStream
- Stream
- CloudBlobContainer
- CloudBlobDirectory
- ICloudBlob
- CloudBlockBlob
- CloudPageBlob
- CloudAppendBlob
This means you can also bind against, for instance, a CloudBlobContainer to output multiple blobs.
Putting messages on a Service Bus
When it comes to Blobs, chances are you wouldn’t want to write a large amount of them based on one trigger. Although I can think of some scenario’s…
For Service Bus however, this scenario is far more likely. Lets have a look at how we can implement that using a ServiceBusBinding. The below Function reads a file. It then splits the contents on a space, and puts every word on a Service Bus queue as a separated message:
public static void Run(
[BlobTrigger("to-process/{name}", Connection = "scs")]Stream addedBlob,
[ServiceBus("process", Connection = "sbcss", EntityType = EntityType.Queue)] ICollector queueCollector,
string name, ILogger log)
{
using (var reader = new StreamReader(addedBlob))
{
var words = (await reader.ReadToEndAsync()).Split(' ');
Parallel.ForEach(words.Distinct(), (word) =>
{
queueCollector.Add(word);
});
}
}
Now that almost feels like magic ? Almost everything is abstracted away for you: connecting and writing to a Service Bus are not even your concern anymore.
For Service Bus out bindings, the types you can bind to are (taken from here):
- out T paramName
- out string
- out byte[]
- out Message
- ICollector or IAsyncCollector
The ICollector<T>
and IAsyncCollector<T>
are for creating multiple messages. A message is created when you call the Add
method. In async functions, use the return value or IAsyncCollector
instead of an out parameter.
The IAsyncCollector<T>.AddAsync()
behavior depends on the type of binding you’re using. And on whether or not the underlying service supports batches. If the service does, calling AddAsync
only prepares the items. In that case, calling FlushAsync()
actually flushes them. When a function completes successfully, FlushAsync()
will be called automatically.
The services that currently support batching:
- SendGrid
- Twilio
- EventHub
There’s an open issue on GitHub that states they Need to drive consistency for IAsyncCollector batching across bindings. Something to keep an eye on.
Hope this helps.