What Is Dependency Injection
Dependency injection (DI) is a software design pattern that allows developers to write loosely coupled code. This is accomplished by specifying which objects a class requires at run time, rather than implementing a concrete dependency. By using DI injection in your applications, you can ensure your code base 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. In this example, we will use this package to implement DI into a simple REST trigger C# function.
The first step is to create an HTTP function, this can be achieved by either 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 directory to the newly created project and run the following to create the function.
func new --name <FUNCTION NAME> --template "HttpTrigger"
After creating the project, you will need to add a reference to the 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 are going to create the service that we will 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 will be used 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 they are requested from the service container. This is recommended for lightweight, stateless services.
- Scoped - Service is created once per client request (connection).
- Singleton - Singleton services are created the first time they’re requested, then every subsequent request uses the same instance.
For the purpose of this example, I will use the transient service lifetime, as I want to create an instance of the Greeter
every time my function is 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, this means that 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!
”, then that means dependency injection for our service has been configured correctly. Nice one!
Wrap Up
Using the dependency injection features provided with the Microsoft.Azure.Functions.Extensions
package means that we can easily 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, please contact me using the comment section of this post or Twitter and I will be happy to help.
History
- 6th June, 2019: Initial version