Azure Functions Dependency Injection

Azure Functions Dependency Injection

One of the most requested building blocs for Azure Functions has been support for Dependency Injection.  Good news it's finally native supported and I'll show you how to use it.

During Microsoft Build 2019 Dependency Injection (DI) Support for Azure Functions was released. The the official documentation can be found here . There has been community solutions to enable DI support, like this one by Boris Wilhelms. As it has not been native supported until now, there has been some issues from time to time as Azure Functions and .net Core is moving forward at a fast pace.

About this example

Please note that this example uses IConfiguration as an example. If you read the the documentation, you'll notice that this dependency gets injected automatically by the framework itself. The purpose of using it here is only to illustrate the way you can register and inject dependencies.

Before Dependency Support

A basic thing needed in most systems is configuration, where we store settings used in our functions. Before the release of DI support a typical setup in an Azure Functions could  look like this:

Configuration support before

As you can see it's based on ExecutionContext and it required that each Function trigger would have to get this dependency and forward it to the configure method, all running in a static context. Dependency injected like this can make it harder to write tests etc.

After Dependency Support

To get Dependency Injection with Azure Functions up and running, just follow these 4 simple steps.

1) Functions Extensions

Added the following Azure Functions Extension package to your project:

Microsoft.Azure.Functions.Extensions

This will provide the new using Microsoft.Extensions.DependencyInjection namespace where all the goodies are.

2) Make a Startup class

First we need to make a Startup where all dependencies will be registered before they can be injected. Here I'll configure a singleton with IConfiguration. You can use the scope you need like Transient, Singleton etc. There are also support for factory methods. To keep things simple we'll just create an instance and register it as a singleton.

[assembly: FunctionsStartup(typeof(Yournamespace.AwesomeFunction.Startup))]
namespace typeof(Yournamespace.AwesomeFunction
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
          
                var config = new ConfigurationBuilder()                           
                           .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
                           .AddEnvironmentVariables()
                           .Build();


            builder.Services.AddSingleton(config);
            
        }
    }
}

The configuration we'll be using in this example is the following where an EmailProvider is added to the configuration:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet",    
    "OrderInputQueue": "order-queue",
    "ServiceBusConnection": ".....",
    "EmailProvider":  "SendGrid"
  }
}

3) Remove static

Before we can use the new dependencies we first need to remove the static context our Azure Functions is in. Simply remove the static keyword in front of your Function declaration.

4) Consume dependencies

To consume dependencies we must add a constructor to our Azure Function. This will be the entry where dependencies are injected. If you have used dependency injection before, this is business as usual using constructor injection.

Finally we can retrieve the configuration and its values using the methods provided by the framework.

using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Configuration;
using System.Collections.Generic;

namespace Yournamespace.AwesomeFunction
{
    public class OrderCrmFunctions
    {
        private IConfiguration _configuration;

        public OrderCrmFunctions(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        [FunctionName(nameof(ProcessOrder))]
        public void ProcessOrder([ServiceBusTrigger("%OrderInputQueue%", Connection = "ServiceBusConnection")]Message message,
            IDictionary<string, object> userProperties)
        {
            var emailProvider = _configuration.GetValue<string>("EmailProvider");

            //......
        }
    }
}

Now lets just sit back and enjoy this moment :)