tomling.dev ⚡
Published on

Using Dependency Injection with C# .NET Azure Functions (with example).

The code for this example can be found here.

What is dependency injection

Dependency injection (DI) is a software design pattern that allows developers to write loosely coupled code. We can accomplish this by specifying which objects a class requires at run time rather than implementing a concrete dependency. Using DI injection in your applications ensures your codebase is maintainable, testable, and easy to update.

Implementing dependency injection in Azure Functions

Microsoft has recently released the Microsoft.Azure.Functions.Extensions NuGet package (at the time of writing version 1.0.0). This package adds native support for dependency injection by building upon the existing ASP.NET Core Dependency Injection features. We will use this package to implement DI into a simple REST trigger C# function in this example.

The first step is to create an HTTP function; this can be achieved through Visual Studio or using the Azure Function CLI.

To use the Azure Function CLI, you will need the Azure Functions Core Tools, which you can install through npm:

npm i -g azure-functions-core-tools --unsafe-perm true

First, you will need to create the function project:

func init <PROJECT NAME> --worker-runtime dotnet

Then change the directory to the newly created project and run the following to make the function:

func new --name <FUNCTION NAME> --template "HttpTrigger"

After creating the project, you must add a reference to Microsoft.Azure.Functions.Extensions package and update to the latest version of the function SDK. You can do this by using the dotnet CLI:

dotnet add package Microsoft.Azure.Functions.Extensions
dotnet add package Microsoft.NET.Sdk.Functions

First, we will create the service to inject into our function. In this example, we will be using a simple class that returns a greeting message. Create the following files (IGreeter.cs and Greeter.cs) in your function project:

namespace DependencyInjectionTutorial
{
    public interface IGreeter
    {
        string Greet();
    }
}
namespace DependencyInjectionTutorial
{
    public class Greeter : IGreeter
    {
        public string Greet()
        {
            return "Greetings from Greeter!";
        }

    }
}

Next, we will need to set up the Startup class, which we will use to register our service in the application. There are three main types of service lifetimes you can choose from when registering a service.

Transient - An instance of your service is created every time it gets requested from the service container. Microsoft recommends this for lightweight, stateless services.

Scoped - Service is created once per client request (connection).

Singleton - Singleton services get created the first time they're requested, then every subsequent request uses the same instance.

For this example, I will use the transient service lifetime, as I want to create an instance of the Greeter every time my function gets invoked.

using DependencyInjectionTutorial;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;

[assembly: FunctionsStartup(typeof(Startup))]

namespace DependencyInjectionTutorial
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            builder.Services.AddTransient<IGreeter, Greeter>();
        }
    }
}

Finally, we need to adapt our HTTP function to use our Greeter service by using constructor injection; we will also need to convert our function to be non-static.

using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

namespace DependencyInjectionTutorial
{
    public class DependencyInjectionTutorialFunction
    {
        private readonly IGreeter _greeter;

        public DependencyInjectionTutorialFunction(IGreeter greeter)
        {
            _greeter = greeter;
        }

        [FunctionName("DependencyInjectionTutorialFunction")]
        public IActionResult Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequest req, ILogger log)
        {
            var greeting = _greeter.Greet();
            log.LogInformation($"Got Greeting: {greeting}");

            return new OkObjectResult(greeting);
        }
    }
}

To test our function, run the following command using the function CLI and navigate to the link provided in the terminal in a browser.

func host start

If you see a message with "Greetings from Greeter!" we have correctly configured dependency injection for our service. Nice one!

Wrap up

The utility provided by Microsoft.Azure.Functions.Extensions package means that we can quickly produce code that is maintainable, testable and easy to refactor. If you require any more additional information about implementing DI into Azure functions, then check out the official Microsoft page here. If you have any questions, don't hesitate to get in touch with me using my Twitter handle @tomlingdev.

Hello 👋 My name is Tom Ling.

I am a Security Engineer at ClearBank.