Discovering Azure Functions extensions
As you may have read in the previous chapter, all triggers and bindings (except for TimerTrigger and HttpTrigger) supported by Azure Functions are available as external packages that must be added in order to be used.
The Azure Functions SDK is based on the Azure WebJobs SDK, so it inherits all the features contained in it and, in particular, it exploits the WebJobs SDK's primitives for the management of extensions.
When the Azure Function host job starts, it needs to discover all the extensions Azure Functions want to use: to achieve this, you must create a class (CustomWebJobsStartup in the next code snippet) and decorated it with the WebJobsStartup attribute to tell the web job what startup class you want to execute in the startup phase:
[assembly: WebJobsStartup(typeof(CustomWebJobsStartup))]
public class CustomWebJobsStartup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
// You can add here all the code you want to execute on startup!
}
}
The CustomWebJobStartup class must implement IWebJobsStartup and you can write your startup code in the Configure method.
The IWebJobsBuilder parameter exposes only the IServiceCollection instance (which can be used to support dependency injection, as you will see in Chapter 6, Testing and Monitoring) but there are a number of extension methods that can be used. One of these methods allows you to add a new extension (a trigger or a binding):
[assembly: WebJobsStartup(typeof(CustomWebJobsStartup))]
public class CustomWebJobsStartup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
builder.AddExtension<MyCustomExtension>();
}
}
An extension is a class that implements the IExtensionConfigProvider interface:
public class MyCustomExtension : IExtensionConfigProvider
{
public void Initialize(ExtensionConfigContext context)
{
// Write here the code to initialize your extension
}
}
In the Initialize method, you must register your custom trigger or binding.
Another important thing to know about in order to use Azure Functions customization in the right way is the binding process that the runtime uses.
The Azure Functions runtime binding process has two different phases:
- Startup binding: The runtime executes this phase only when the host starts. In this phase, the runtime registers the built-in binding (TimerTrigger and HttpTrigger) and you must add your custom extensions.
- Runtime binding: The runtime executes this phase every time a function is triggered by an event.
Let's analyze the individual phases in detail. During the startup binding, the runtime performs the following steps (the startup binding phase):
- At the start of the host job, the runtime registers its own integrated binding providers. In this step, you can register your custom bindings using the preceding technique.
- The runtime uses reflection to find all the methods that implement an Azure Function within the referenced assemblies.
- For each Azure Function found in the previous step, and for each parameter of the function, the runtime will attempt to identify, among the registered binding providers (using the ITriggerBindingProvider and IBindingProvider interfaces provides by the Azure WebJobs SDK), the provider needed to resolve the binding. If the provider is found, the runtime will use it to bind the parameter.
- When all the functions have been processed, the runtime creates an internal representation for each of them with all the information necessary for execution during the runtime phase. Functions that do not use bindings correctly (for example, if an unregistered binding is used), are discarded by the runtime and are not available for execution.
- For each trigger, the runtime creates the corresponding listener and runs it. Now the function is ready to react to events.
Every time an Azure Function is triggered, the runtime performs the following steps (the runtime binding phase):
- The runtime retrieves the complete definition of the function that it created during the startup phase and, for each binding, executes the BindAsync method (ITriggerBinding.BindAsync/IBinding.BindAsync).
- The binding has the responsibility of converting the input values to the values actually used by the function.
- If all binding methods are executed without any exceptions, the function is executed.