17

Using the binding options for an Azure Function one can specify the name of a Blob to be written based on parameters derived from the trigger (e.g. the queue message that triggered the function); the documentation shows an example of that.

My question is: what is the best way to handle the case where the blob name is not known in advance, but in fact is calculated as part of the function's execution?

And related: what to do if the function may or may not produce an output blob (or multiple output blobs!), based on the outcome of its calculation?

As far as I can see now Azure Function's binding mechanism doesn't help much in these cases and the easiest approach is to refer to an assembly that does the azure blob writing "the classical way". But is there a more idiomatic way?

Janusz Nowak
  • 2,595
  • 1
  • 17
  • 36
Luc C
  • 1,133
  • 8
  • 22
  • Updated answer with missing information is [here](http://stackoverflow.com/questions/39855409/how-do-i-use-binder-to-perform-dynamic-bindings-in-my-c-sharp-function). – Sean Feldman Oct 04 '16 at 22:22

1 Answers1

24

You can actually already to this in C# Azure Functions, and we have a tracking item here in our repo to enable this for Node.js Functions as well. We'll get to that soon.

Below is an example working function that binds to a blob with the path specified at runtime. Since under the covers Azure Functions is built on the Azure WebJobs SDK, you'll notice that this relies on using the WebJobs SDK Binder something that you might not be familiar with. Please see the WebJobs SDK for more documentation on IBinder/Binder. In the WebJobs SDK, declarative attributes are used for bindings (e.g. QueueAttribute/TableAttribute/BlobAttribute, etc.). You can specify all of these at runtime via Binder. In Azure Functions, we use external metadata to describe bindings, but in this advanced scenario you have a hybrid. Note that when using Binder there is no corresponding binding in function.json. For more details on Binder dynamic bindings see this SO question/answer.

In general, you'll find that many awesome WebJobs SDK features are usable in Azure Functions - our doc just needs to catch up to make people aware of this :)

One other thing to note: there is some inbuilt support for generating random new identifiers for outputs. E.g. if you were to set your output blob path to test-output/{rand-guid} the system will automatically generate a new ID for you. If that meets your needs, then you don't need Binder.

using System;
using System.IO;
using System.Net;
using Microsoft.Azure.WebJobs;

public static async Task<HttpResponseMessage> 
       Run(HttpRequestMessage req, Binder binder, TraceWriter log)
{
    log.Verbose($"C# HTTP function processed RequestUri={req.RequestUri}");

    using (var writer = await binder.BindAsync<TextWriter>(
                  new BlobAttribute("test-output/result")))
    {
        writer.Write("Hello World!!");
    }

    return new HttpResponseMessage(HttpStatusCode.OK);
}

For your second question, if you want to conditionally write to an output binding, just don't assign any value to the binding - no output should be produced.

mathewc
  • 13,312
  • 2
  • 45
  • 53
  • 2
    Should you have a `using` on the writer to make sure it gets closed? – David Ebbo Apr 05 '16 at 18:45
  • 2
    cool! I didn't know the IBinder "trick" what i did was: `var container = outBlob.Container; var newOutBlob = container.GetBlockBlobReference(newFileName);` :$ –  Jul 21 '16 at 15:42
  • @mathewc Does IBinder work for other outputs as well? I tried doing something similar with SaasFile output but could not get it to work. The question is [here](http://stackoverflow.com/questions/39669606/how-can-i-set-a-folder-name-and-file-name-in-azure-functions-saas-file-bindings) if you get a chance. – DBueno Sep 26 '16 at 17:24
  • @mathewc can you update your answer to include the function.json that matches the code above? I'm trying to more clearly understand how IBinder parameter relates to the blob output binding in function.json. – Chris Gillum Dec 10 '16 at 01:29
  • Another question: is it %rand-guid% or {rand-guid}? You mentioned %rand-guid% but the documentation says {rand-guid}. https://learn.microsoft.com/en-us/azure/azure-functions/functions-triggers-bindings#random-guids – Chris Gillum Dec 10 '16 at 01:37
  • 2
    Binder/IBinder parameters don't have a representation in function.json. I'll clarify my answer above. Also we did change the rand-guid syntax I'll fix that as well. A more descriptive post on Binder scenarios is here: https://stackoverflow.com/questions/39855409/how-do-i-use-binder-to-perform-dynamic-bindings-in-my-c-sharp-function – mathewc Dec 10 '16 at 01:39
  • @mathewc - Is there a way to bind Storage Queues instead of Blob Storage using Azure functions? I tried using the QueueAttribute instead but I am getting an error message when trying to use binder to bind to the queue. CloudQueue outputQueue = binder.Bind(queueAttribute); Error : The type or namespace name 'CloudQueue' could not be found (are you missing a using directive or an assembly reference?) – Vineet Jan 11 '17 at 17:14
  • Yes, it works for all attributes. You just have to add the correct #r assembly refs and using statements. In this case `#r "Microsoft.WindowsAzure.Storage"` and `using Microsoft.WindowsAzure.Storage.Queue`. I mentioned this above in my answer already. – mathewc Jan 11 '17 at 18:06
  • would love to know what you mean by `just don't assign any value to the binding`?? – Alex Gordon Mar 29 '19 at 22:31
  • if possible, could you please update this answer if you believe there's a more idiomatic way to accomplish conditional output bindings? – Alex Gordon Mar 29 '19 at 22:32
  • This answer shows how you can use the attribute to set the connection string: https://dev.to/azure/runtime-binding-with-azure-functions-3gno var attribute = new BlobAttribute("messages/{sys.randguid}.txt", FileAccess.Write); attribute.Connection = "AzureStorageConnectionString"; – johnstaveley Feb 13 '22 at 09:32
  • 1
    A easy step-by-step example can be found at: https://ianescober.github.io/posts/dynamic-binding-in-azure-functions/ - does need an minor adjustment, since the binder.BindAsync now only takes a single attribute (instead of an array), but works like a charm. – P. Dörr May 31 '22 at 15:02