Azure Functions Dependency Injection is a game-changer for managing dependencies in your serverless applications.
To get started, you'll need to install the Microsoft.Extensions.DependencyInjection package. You can do this by running the command dotnet add package Microsoft.Extensions.DependencyInjection in your terminal.
Dependency injection is a design pattern that allows you to decouple components and make your code more modular and testable. This is particularly useful in Azure Functions where you may have multiple triggers and bindings.
By using dependency injection, you can easily swap out different implementations of a service without affecting the rest of your code. This makes it easier to test and maintain your functions.
Prerequisites
Before you can use dependency injection in Azure Functions, you need to install the right NuGet packages. Specifically, you'll need to install Microsoft.Azure.Functions.Extensions, Microsoft.NET.Sdk.Functions with a version of 1.0.28 or later, and Microsoft.Extensions.DependencyInjection, which should be version 2.x or later.
To give you a better idea, here are the required NuGet packages:
- Microsoft.Azure.Functions.Extensions
- Microsoft.NET.Sdk.Functions (version 1.0.28 or later)
- Microsoft.Extensions.DependencyInjection (version 2.x or later)
If you're running your function app in the Consumption or Premium plans, be aware that modifying configuration values used in triggers can cause scaling errors.
Dependency Injection
Dependency Injection is a software development pattern that makes code easier to read and reuse. It's supported by Azure Functions V2 and above, which is built on the .NET Core Dependency Injection features.
You can use dependency injection in Azure Functions by adding a constructor to your Function and referencing the services from there. To make it work with dependency injection, you need to move from static scope to instance scope and use constructor injection.
Dependency injection allows you to reuse connection securely and eliminate the need to constantly spin up a new connection or instance of that service. This helps to save lots of execution time.
Here are some benefits of using dependency injection in Azure Functions:
- Code Reuse and Readability
- Save Resources
- Improve Testability
- Improve Performance
- Quite Easy to develop
By using dependency injection, you can reduce execution time by ~500ms per function execution, and reduce stress on the endpoints (Storage/Databases, APIs, ..). This can help to save approx. 60 days/Month/10M executions.
To implement dependency injection with Azure Functions, you need to create a Startup class that configures dependency injection. You can also use the FunctionsStartup class to function.
Implementation
To implement dependency injection in Azure Functions, you need to use Azure Functions V2 and above, which supports ASP.NET Core-like dependency injection. Dependency injection in Azure Functions is built on the .NET Core Dependency Injection features.
You can inject instances to Azure Functions by adding a constructor to your Function and referencing the services from there. To make it work with dependency injection, you need to move from static scope to instance scope and use constructor injection.
The key benefits of using dependency injection in Azure Functions include code reuse and readability, saving resources, improving testability, improving performance, and making it quite easy to develop.
Here are the core steps to implement dependency injection in Azure Functions:
- Create a new file called ITipsService.cs and define the interface for the service.
- Create a file called TipService.cs and implement the interface. This will contain the logic for the service.
- Create a file called Startup.cs and configure dependency injection by adding the service to the app.
- In the Function class, use a constructor to get the injected service.
Note that the FunctionsStartup attribute is used to indicate a startup class that will run when the function app starts. In this class, you can override the Configure method to register services and inject them into your function.
Here are some benefits of using dependency injection in Azure Functions:
Best Practices
To get the most out of Azure Functions Dependency Injection, it's essential to follow best practices.
Avoid using the same instance of a service across multiple Functions, as this can lead to unexpected behavior and errors.
Use a transient lifetime for services that are not meant to be shared between Functions, such as those that interact with external APIs.
Working with Options
You can extract values from the IConfiguration instance into a custom type, making it easy to test your services by making these values injectable. Settings read into the configuration instance must be simple key/value pairs.
For functions running in an Elastic Premium plan, application setting names can only contain letters, numbers (0-9), periods (.), colons (:), and underscores (_). This is a consideration to keep in mind when structuring your app settings.
Copy the app settings values to a custom type, which can be done using a class that includes a property named consistently with an app setting. For example, a local.settings.json file might structure the custom setting as follows.
Use the Bind method to copy values that have matching property names from the configuration into the custom instance. The options instance is now available in the IoC container to inject into a function.
The options object is injected into the function as an instance of the generic IOptions interface, and you can use the Value property to access the values found in your configuration.
Customizing Configuration Sources
Customizing Configuration Sources is crucial when working with Azure Functions. You can override the ConfigureAppConfiguration method in your function app's StartUp class to specify additional configuration sources.
This allows you to add configuration values from base and optional environment-specific app settings files. To do this, add configuration providers to the ConfigurationBuilder property of IFunctionsConfigurationBuilder.
A FunctionsHostBuilderContext is obtained from IFunctionsConfigurationBuilder.GetContext(), which you can use to retrieve the current environment name and resolve the location of configuration files in your function app folder. This is essential for configuration files such as appsettings.json to be copied to the function app's output folder.
You can update your .csproj file to ensure these files are copied automatically. This is a simple step that can save you a lot of time and effort in the long run.
Caveats
When working with Azure Functions, it's essential to keep in mind some key caveats that can impact the performance and functionality of your application.
Avoid using services registered at startup during the startup process, as they may not be available yet. This includes trying to log messages in a logger that is being registered during startup.
The dependency injection container only holds explicitly registered types, so Functions-specific types like BindingContext and ExecutionContext aren't available during setup or as injectable types.
Configuring ASP.NET authentication isn't supported, as the Functions host configures ASP.NET authentication services to properly expose APIs for core lifecycle operations.
Other configurations in a custom Startup class can override this configuration, causing unintended consequences, such as breaking authentication between the portal and the host.
Here are some key things to remember:
- Don't try to log messages in a logger being registered during startup.
- Functions-specific types like BindingContext and ExecutionContext aren't available during setup or as injectable types.
- Configuring ASP.NET authentication can break authentication between the portal and the host.
Wrapping Up
Using Dependency Injection can significantly decrease the initialization time of Azure Functions, especially when the majority of the heavy lifting work is setting up services.
This is because Dependency Injection makes code easier to read and reuse, improving performance in the process.
By leveraging Dependency Injection, developers can improve the efficiency of their Azure Functions projects, leading to better overall performance.
In Azure Functions V2 and above, the Dependency Injection mechanism is similar to that used in ASP.NET Core, making it easier to implement and manage.
Sources
- https://www.markheath.net/post/ef-core-di-azure-functions
- https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection
- https://www.linkedin.com/pulse/azure-functions-quick-guide-part-1-prem-prakash
- https://www.twilio.com/en-us/blog/draft-dependency-injection-in-azure-functions-with-csharp
- https://microsoft.github.io/AzureTipsAndTricks/blog/tip351.html
Featured Images: pexels.com