In this article we will look at how to do Dependency Injection with Azure Functions as described in the article at
https://docs.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection.
The code for this article can be found at
https://github.com/mattruma/SampleDependencyInjectionFunctionApp.
To make the most of this article, it will be helpful to be familiar with:
- Azure Functions
- Dependency Injection and Dependency Injection in ASP.NET Core
- Visual Studio or VS Code
This application supports three different ways to add a ToDo item using a specified Data Store.
The data store could write to Azure Table Storage, Azure Cosmos DB, Azure SQL or any data storage for that matter.
For this article, the data stores do not write to any data storage.
Let’s look at the code.
Open the solution in Visual Studio.
We have two classes, ToDoSample1DataStore
and ToDoSample2DataStore
, each implementing the IToDoDataStore
interface.
The IToDoDataStore
has a single method, Add
, which accepts a ToDo
object and an ILogger
and return a ToDo
object.
Each of the implementations of our Add
methods, in the ToDoSample1DataStore
and ToDoSample2DataStore
, will output the data store that handled the add and apply the name of the data store to the ToDo
object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
using Microsoft.Extensions.Logging; namespace SampleDependencyInjectionFunctionApp.Data { public class ToDoSample1DataStore : IToDoDataStore { public ToDo Add( ToDo toDo, ILogger logger) { logger.LogInformation($"Adding ToDo using {nameof(ToDoSample1DataStore)}..."); logger.LogInformation("ToDo added."); toDo.DataStore = nameof(ToDoSample1DataStore); return toDo; } } } |
Let’s move on to the functions.
We have three Azure Functions, wonderfully named, Function1
, Function2
and Function3
.
Each function will call the Add
method on the appropriate data store.
Function1
makes use of theToDoSample1DataStore
Function2
makes use of theToDoSample2DataStore
Function3
depends on dependency injection which which will determine ifToDoSample1DataStore
orToDoSample2DataStore
will be used.
Function1
is the standard boiler plate HttpTrigger function, except for one little change, instead of taking an HttpRequest
object, it will take a ToDo
object.
The function automatically converts the HttpRequst
to a ToDo
object for us, pretty sweet!
Function1
creates a concrete instance of the ToDoSample1DataStore
and adds the passed ToDo
object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.Http; using Microsoft.Extensions.Logging; using SampleDependencyInjectionFunctionApp.Data; namespace SampleDependencyInjectionFunctionApp { public static class Function1 { [FunctionName("Function1")] public static IActionResult Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] ToDo toDo, ILogger logger) { logger.LogInformation("Function1 function processed a request."); if (toDo == null) { return new BadRequestResult(); } var toDoDataStore = new ToDoSample1DataStore(); toDo = toDoDataStore.Add( toDo, logger); return new OkObjectResult(toDo); } } } |
If we call Function1
from PostMan we can see our ToDo
object’s DataStore property is stamped with the value ToDoSample1DataStore
, letting us know which IToDoDataStore
‘s Add
method was called.
Function2
is almost exactly like Function1
, except it creates an instance of ToDoSample2DataStore
to handle adding ToDo
objects, which will stamp the ToDo
object’s DataStore property with a value of ToDoSample2DataStore
.`
Now for the real fun! Let’s look at Function3
!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.Http; using Microsoft.Extensions.Logging; using SampleDependencyInjectionFunctionApp.Data; namespace SampleDependencyInjectionFunctionApp { public class Function3 { private readonly IToDoDataStore _toDoDataStore; public Function3( IToDoDataStore toDoDataStore) { _toDoDataStore = toDoDataStore; } [FunctionName("Function3")] public IActionResult Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] ToDo toDo, ILogger logger) { logger.LogInformation("Function3 function processed a request."); if (toDo == null) { return new BadRequestResult(); } toDo = _toDoDataStore.Add( toDo, logger); return new OkObjectResult(toDo); } } } |
Some things to point out in Function3
that are different than Function1
and Function2
:
- The class is no longer
static
. - The
Run
method is no longerstatic
. - The class has a bona fide constructor.
This looks a lot like what we see in a Controller for a .NET Web App or Web Api.
So how does Function3
know where to get an IToDoDataStore
from?
Just like in a .NET Web App or Web Api it all happens in Startup.cs
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
using Microsoft.Azure.Functions.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; using SampleDependencyInjectionFunctionApp.Data; // https://docs.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection [assembly: FunctionsStartup(typeof(SampleDependencyInjectionFunctionApp.Startup))] namespace SampleDependencyInjectionFunctionApp { public class Startup : FunctionsStartup { public override void Configure(IFunctionsHostBuilder builder) { builder.Services.AddTransient<IToDoDataStore, ToDoSample1DataStore>(); // TODO: Uncomment the below, and comment the above to use ToDoSample2DataStore as the default implement of IToDoDataStore // builder.Services.AddTransient<IToDoDataStore, ToDoSample2DataStore>(); } } } |
Note: I apologize if the “<” signs are not showing correctly, it is something with EnlighterJS.
If we want a ToDoSample1DataStore
to be provided for each IToDoDataStore
we simply need to add it to the services, using the AddTransient
method.
For more information service lifetimes, Transient, Scoped and Singleton, see
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2#service-lifetimes.
Let’s call Function3
from PostMan.
You can see that the ToDo
object’s DataStore property was stamped with the ToDoSample1DataStore
data store.
Let’s make it use ToDoSample2DataStore
.
Back in the Startup.cs
file, comment the line using ToDoSample1DataStore
and uncomment the line using ToDoSample2DataStore
.
One more time, let’s call Function3
from PostMan.
Note the DataStore property of the ToDo
object contains the value ToDoSample2DataStore
.
While a simplistic example of Azure Functions and Dependency Injection, it should be enough to get you started!
Discover more from Matt Ruma
Subscribe to get the latest posts sent to your email.