Dependency Injection (DI) has been available in .NET Core Web Applications for a while and now seems to have finally made it’s way to Azure Functions as well and is available in the current Azure Functions Runtime 2.0.12382. The GitHub issue tracking DI implementation in Azure Functions is here.
I’ve created a test project in an effort to figure out how it works but happily it seems pretty similar to using it in a Web Application.
In my case I wanted to inject a Serilog logger and a service to help in accessing the JSONPlaceholder API.
The first step is to add a new Startup.cs class to the project that inherits from IWebJobsStartup, this will allow you to added the required DI services and build the ServiceProvider.
Startup.cs
namespace JsonPlaceHolderDependencyInjection
{
public class Startup : IWebJobsStartup
{
public Startup()
{
// Initialize serilog logger
Log.Logger = new LoggerConfiguration()
.WriteTo.Console(Serilog.Events.LogEventLevel.Debug)
.MinimumLevel.Debug()
.Enrich.FromLogContext()
.CreateLogger();
}
public void Configure(IWebJobsBuilder builder)
{
ConfigureServices(builder.Services).BuildServiceProvider(true);
}
private IServiceCollection ConfigureServices(IServiceCollection services)
{
services
.AddLogging(loggingBuilder =>
loggingBuilder.AddSerilog(dispose: true)
)
.AddTransient<IJsonPlaceholderClient, JsonPlaceholderClient>(client =>
new JsonPlaceholderClient(Environment.GetEnvironmentVariable("BaseAddress"))
)
.AddTransient<IJsonPlaceholderService, JsonPlaceholderService>();
return services;
}
}
}
In your function you then need to provide an assembly reference to the Startup class [assembly: WebJobsStartup(typeof(Startup))]
and you can then use DI as expected.
GetAlbums.cs
[assembly: WebJobsStartup(typeof(Startup))]
namespace JsonPlaceHolderDependencyInjection.Function
{
public class GetAlbums
{
private readonly IJsonPlaceholderService _jsonPlaceholderService;
private readonly ILogger _logger;
public GetAlbums(IJsonPlaceholderService jsonPlaceholderService, ILoggerFactory loggerFactory)
{
_jsonPlaceholderService = jsonPlaceholderService;
_logger = loggerFactory.CreateLogger("GetAlbums");
}
[FunctionName("GetAlbums")]
public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "get", Route = "GetAlbums/{id?}")] HttpRequest req, int? id)
{
_logger.LogInformation("C# HTTP trigger function processed a request.");
//log.LogInformation("C# HTTP trigger function processed a request.");
if (id == null)
{
return (ActionResult)new OkObjectResult(await _jsonPlaceholderService.GetAlbums());
}
else
{
return (ActionResult)new OkObjectResult(await _jsonPlaceholderService.GetAlbumById((int)id));
}
}
}
}
9 Comments
Mrunal · 26th June 2019 at 12:35 pm
This is great stuff.
How can we test this? As far as I understand the concept the main reason for having dependency injection is to make is loosely coupled and hence easy to test.
How can I inject my mocks?
Shinigami · 26th June 2019 at 5:18 pm
You can add tests by referencing the function in a test project and making HTTP calls to it.
I’ve created an example here.
https://github.com/mattosaurus/JsonPlaceHolderDependencyInjection/tree/master/JsonPlaceHolderDependencyInjection.Tests
The Microsoft guide to function testing is also pretty handy.
https://docs.microsoft.com/en-us/azure/azure-functions/functions-test-a-function
Mrunal · 27th June 2019 at 12:03 pm
Thanks for the reply.
The guidance given at the mentioned URL is good information. One question though, is there a way to utilize TestServer?
I have seen many ASP.NET core application utilizing TestServer.
Shinigami · 2nd July 2019 at 10:03 am
I haven’t tried using TestServer for Azure functions but I don’t see why it wouldn’t be possible given that it’s just another way of making requests. From a quick look it seems like you should be able to use it as expected.
Luis Javier Posada Marin · 27th July 2019 at 5:13 am
Hi,
I have some errors:
[23:13:01 INF] Host initialized (6646ms)
[23:13:01 INF] Host started (6712ms)
[23:13:01 INF] Job host started
[23:13:01 ERR] The following 2 functions are in error:
GetAlbums: The method ‘Run’ cannot be found.
GetPhotos: The method ‘Run’ cannot be found.
What is the error?
Shinigami · 27th July 2019 at 9:00 am
The code above is just a snippet, the full code base is on GitHub.
https://github.com/mattosaurus/JsonPlaceHolderDependencyInjection
Umair Muhammad Riaz Syed · 21st January 2020 at 10:19 pm
how can we implement this using FunctionsStartup and IFunctionsHostBuilder?
Shinigami · 22nd January 2020 at 10:54 am
There’s now some offical Microsoft documentation on how to do this.
https://docs.microsoft.com/bs-latn-ba/azure/azure-functions/functions-dotnet-dependency-injection
I’ve also created a new version of this post with an updated version of Startup.cs
https://blog.bitscry.com/2020/01/22/dependency-injection-in-azure-functions-v3/
Dependency Injection in Azure Functions v3 – bitScry · 3rd July 2020 at 11:55 am
[…] It’s pretty similar to how it was done previously, below is updated code from my previous v2 example. […]