Cloud & Engineering

Justin Yoo

Dependency Injections on Azure Functions V2

Posted by Justin Yoo on 06 April 2018

Azure Functions, Blog, Technology, V2, ASP.NET Core, Dependency Injection

Dependency Injections on Azure Functions are not very intuitive. I’ve written many blog posts about dependency management on Azure Functions to improve testability and this was my latest one. However, they are mostly about V1, which supports .NET Framework. Azure Functions V2 is now on public preview and I’m going to write another post for DI on Azure Functions V2, by taking advantage of the simple dependency injection feature that ASP.NET Core provides out-of-the-box.

The Problem

Due to the static nature of Azure Function triggers, it’s not easy to manage dependencies. If we could inject an IoC container itself, when an Azure Function instance is being loaded, this would be ideal. I am pretty sure that the Azure Functions Team at Microsoft currently works hard to make this happen. In the meantime, we need to find out how to work around this. One of the easiest and most popular tricks is to use a static property on each trigger. This static property is basically an instance of an IoC container. Once the property gets instantiated, each function trigger resolves dependencies within the function.

The Workaround – IoC Container from ASP.NET Core

When an ASP.NET Core application is up and running, it bootstraps all dependencies at first within the StartUp class using the IServiceCollection instance. This instance also has some DI functions like AddTransient(), AddScoped(), and AddSingleton(). As Azure Functions V2 comes with ASP.NET Core, we can directly make use of this feature. The only difference is that, in Azure Functions, we have to bootstrap dependencies by ourselves. Let’s have a look.

The source code used in this post can be found here.

Scenario

I am asked to write an Azure Function code, given a username or organisation name on GitHub, to return the list of repositories. Let’s simplify the user story here:

AS a user, when I give a GitHub username or organisation name,
I WANT TO see the list of GitHub repositories

In order to achieve this user story, I need to write an HTTP trigger function to send an HTTP request to a GitHub API. OK, first things first.

HTTP Trigger Function

This is the HTTP trigger function, with all dependencies inside.

No good. I am not happy with that because the HttpClient should be injected from outside and there are a few things to be injected outside. This needs to be refactored. Let’s change it. First of all, I need to add a static property of IServiceProvider to the trigger, which acts as an IoC container. By the way, the IServiceProvider should be instantiated by IServiceCollection. Therefore, it’s a good idea to create a ContainerBuilder class to build it.

Container Builder

This is the interface design. It accepts a module from outside, RegisterModule(), which contains all dependencies then build a container, Build(), to return IServiceProvider.

Therefore, its implementation creates a new IServiceCollection instance, loads all dependencies from the IModule, then builds IServiceProvider instance, like below:

Module

Then, how does the IModule works? From one trigger to another, they don’t have the same dependencies at all. In order to keep the collection of dependencies as light as possible, modularising dependencies is a better practice. Let’s have a look. The IModule interface defines one method, Load() and it takes one parameter of IServiceCollection.

The Module class implements IModule and does nothing but works as a placeholder. This is a sort of base module which can be used, in case there is no suitable module found.

Another implementation of IModule is CoreAppModule, which loads an instance of HttpClient as a singleton. The reason why it should be registered as a singleton can be found here.

I’ve created the ContainerBuilder class above and it’s ready to play. Let’s refactor the existing trigger.

HTTP Trigger Function – Refactored #1

The trigger function now needs to have a static property of IServiceProvider like:

Now, I can inject HttpClient instance into the trigger, which has become more testable. But I’m still not happy with the result. Why? Let’s see the requestUrl variable. It’s hard-coded. What if the endpoint URL is changed for some reason? It should be configurable by reading from either an environment variable or a separate settings files like appsettings.json which is a similar way to how an ASP.NET Core application does.

Configurations

I have a config.json file that looks like:

Its corresponding POCO class looks like:

Module – Refactored #1

ASP.NET Core supports a configuration builder OOTB, so I can just use it in the module class.

In this example, I just use the AddJsonFile() method, but other methods like AddXmlFile() or AddEnvironmentVariables() can be used depending on your preferences. Even I can use YAML file for configuration settings. Now, the GitHub endpoint URL is all configurable.

HTTP Trigger Function – Refactored #2

With this in mind, let’s do another refactoring and this is the result:

For now it’s a sort of working code with full testability. Therefore, the test code for this function might look like:

As you can see above, the static property has got a mocked instance, and the function parameters also have received the mocked ones for testing. This is how dependency injection approach is used for Azure Function triggers.

Service Locator

However, this approach still imposes an issue – Service Provider Pattern, which is known as an anti-pattern. In the function trigger code refactored above, I explicitly resolved two instances.

From the caller’s point of view, the function trigger in this case, it’s not necessary to know which dependencies I need to resolve, but just run them. With this point, the function trigger needs more refactoring to hide dependency resolutions. This is also a good practice for encapsulation of features that should be hidden.

Function Factory

Let’s have a look at the interface design of IFunctionFactory.

It returns a function implementing the IFunction interface, which is also registered into the IoC container. What does IFunction do? All logics in the function trigger move into there. For example,

As you can see, all the logics resided in the function trigger has moved into the CoreGitHubRepositoriesFunction class. Now the implementation of the IFunctionFactory might look like:

This factory class firstly loads dependencies, then resolves a function with the given type when it’s called.

Module – Refactored #2

Now, I need to update the CoreAppModule class to register the IFunction instance.

By doing so, all necessary dependencies have been registered into the IoC container.

HTTP Trigger Function – Refactored #3

With these changes, let’s refactor the function trigger again. Instead of directly using the IServiceProvider as a static property, it uses the IFunctionFactory this time.

What the function trigger needs to do is to pass parameters and invoke the function that contains all the logics. It doesn’t have to know what’s going on inside the function. Testing the function trigger gets much easier.

Of course, all the logics also need to be tested, but it’s much easier because they are NOT static classes any longer. I’m not going to show how to test the rest here. Instead, I’ll let you test them.

More Complex Dependency Injection Scenarios

Someone with hawk eyes might have been wondering why I used IGitHubRepositoriesFunction, instead of IFunction. The dependency injection feature that ASP.NET Core provides is very simple. There is no control over multiple implementations with a same interface. For example, there might be multiple functions implementing the same IFunction interface like:

If I need to differentiate them from each other, the current trick is to create another interface like IFunctionABC, IFunctionPQR and IFunctionXYZ inheriting IFunction and pass them, instead of directly using IFunction. Alternatively, I can write a custom logic around them.

There is another scenario. Functions tend to live in a same assembly, ie. a same .dll file. If I could scan a .dll file and automatically register all functions, it would be much easier. Unfortunately, this is not supported by ASP.NET Core either. If you really want to use those features, a 3rd-party library like Autofac needs to be considered. However, also unfortunately, it doesn’t seem to get along with V2 yet.

Therefore, here’s the suggestion. If you want to use the IoC container from the 3rd-party library, stay on V1. If you want to use the IoC container provided by ASP.NET Core, use V2.


To summarise, I’ve walked through how dependency injections are working on Azure Functions V2, with ASP.NET Core’s DI feature. Obviously this is not an ideal solution yet and I know the Azure Functions Team is working really hard to enable this feature sooner rather than later. I hope this feature is released soon.

 

If you like what you read, join our team as we seek to solve wicked problems within Complex Programs, Process Engineering, Integration, Cloud Platforms, DevOps & more!

 

Have a look at our opening positions in Deloitte. You can search and see which ones we have in Cloud & Engineering.

 

Have more enquiries? Reach out to our Talent Team directly and they will be able to support you best.

Leave a comment on this blog: