Dependency injection in Azure Function v3
Photo by Oscar Nord on Unsplash

Dependency injection in Azure Function v3

In this article, I am going to show how DI (Dependency Injection) can easily be achieved within an Azure Function project and explain its benefits. This article is a follow-on from the previous article where I set up a wireframe project and I will be using the same wireframe in this article.

Firstly, we must look at Dependency Injection. Dependency Injection (which I shall refer to as DI) is a software design pattern that allows us to develop loosely coupled code. The purpose of DI is to make code maintainable and reduce tight coupling between components.

The biggest advantages of DI are as follows:

  • Reduces data coupling
  • Increases code reusability
  • Improves code maintainability
  • Make unit testing possible

DI is extremely easy to set up and greatly increases the reusability of code. In the example to come, we are going to create the following components and consume them in the application on run time.

  • ITestService (Interface)
  • TestService (Interface)

Viewing the file structure as is shown below, right-click "Services", hover "Add" and click "Add a new class..." and name it "TestService.cs".

No alt text provided for this image
No alt text provided for this image
No alt text provided for this image

Once you have added the class, paste the following code into it, making sure to replace the entire class but not the namespace:

public class TestService
? ? {
? ? ? ? public string SayHello(string name)
? ? ? ? {
? ? ? ? ? ? string retVal = !string.IsNullOrEmpty(name) ? "Hello " + name + " from Injected Service" : "Oops, you did not provide a name!";
? ? ? ? ? ? return retVal;
? ? ? ? }
? ? }        

Now, right-click on the folder called "Interfaces", hover "Add" and click "New Item...".

No alt text provided for this image

Next, in the search bar on the top right, search for "Interface" and click on the option that remains. Name the file "ITestService.cs" and click "Add".

No alt text provided for this image

Once you have added the interface, paste the following code into it, making sure to replace the entire interface but not the namespace:


public interface ITestService
? ? {
? ? ? ? public string SayHello(string name);
? ? }
        

Now, return to the "TestService.cs" class and tell it to inherit from the interface as follows:


public class TestService : ITestService
        

This is the only line we will need to modify in this file. Now that we have our interface and service set up, we now need to register them within our "Startup.cs" so that we can inject them wherever we need them! Once you have navigated to "Startup.cs", we will add the following code inside the "Configure" method:


builder.Services.AddSingleton<ITestService, TestService>();
        

Next step is to add our using statements as follows:


using Microsoft.Extensions.DependencyInjection;
using V3.Practice.Application.Interfaces;
using V3.Practice.Application.Services;
        

This will allow us to add a Singleton, Scoped or Transient to our application as well as allow us to reference our Interface and Service to allow us to register them. You may need to adjust the usings for the Interface and Service as your project may be named differently.

The "Startup.cs" should resemble the following now that we have added what we need:


using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using V3.Practice.Application.Interfaces;
using V3.Practice.Application.Services;


[assembly: FunctionsStartup(typeof(V3.Practice.Startup))]
namespace V3.Practice
{
? ? public class Startup : FunctionsStartup
? ? {
? ? ? ? public override void Configure(IFunctionsHostBuilder builder)
? ? ? ? {
? ? ? ? ? ? builder.Services.AddSingleton<ITestService, TestService>();
? ? ? ? }
? ? }
}
        

The next step to achieving DI is to now use Constructor Injection which will allow us to use it anywhere in the class as the dependency is supplied through the class's constructor when creating the instance of the class. Navigate to the file named "TestFunction.DI.cs", we will be adding the dependency to the class constructor as follows:

First, we need to create a private variable for the service so we can reference it and consume it in our "TestFunction.cs" so above the constructor we place the following:


private ITestService _testService;
        

Now we need to modify the constructor so we can assign the Interface on class initialization, we will do as follows:

Modify the constructor as follows:


public TestFunction(ITestService testService)
? ? ? ? {
? ? ? ? ? ? _testService = testService;
? ? ? ? }
        

What we do in the code above, is we inject the dependency and assign it to our private variable so we can use it. Now in the "TestFunction.cs" we can call "_testService" anywhere in that page. Below is the default code for a function that I will be using to modify to call the Injected Service:


using System.IO;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;


namespace V3.Practice.Functions.PracticeFunction
{
? ? public partial class TestFunction
? ? {
? ? ? ? [FunctionName("TestFunction")]
? ? ? ? [OpenApiOperation(operationId: "Run", tags: new[] { "name" })]
? ? ? ? [OpenApiSecurity("function_key", SecuritySchemeType.ApiKey, Name = "code", In = OpenApiSecurityLocationType.Query)]
? ? ? ? [OpenApiParameter(name: "name", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **Name** parameter")]
? ? ? ? [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "text/plain", bodyType: typeof(string), Description = "The OK response")]
? ? ? ? public static async Task<IActionResult> Run(
? ? ? ? ? ? [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
? ? ? ? ? ? ILogger log)
? ? ? ? {
? ? ? ? ? ? log.LogInformation("C# HTTP trigger function processed a request.");


? ? ? ? ? ? string name = req.Query["name"];


? ? ? ? ? ? string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
? ? ? ? ? ? dynamic data = JsonConvert.DeserializeObject(requestBody);
? ? ? ? ? ? name = name ?? data?.name;


? ? ? ? ? ? string responseMessage = string.IsNullOrEmpty(name)
? ? ? ? ? ? ? ? ? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
? ? ? ? ? ? ? ? : $"Hello, {name}. This HTTP triggered function executed successfully.";


? ? ? ? ? ? return new OkObjectResult(responseMessage);
? ? ? ? }
? ? }
}
        

The first thing we will need to do is to make the method non-static otherwise we will not be able to use our Injected Service. Then we will need to make a small modification after the "name" has been assigned from the requestBody and that modification is as follows:

Delete the variable and assignment of "responseMessage" and replace it with the following:


string responseMessage = _testService.SayHello(name);
        

Now when we run the Function and call it using either the Swagger UI or a Postman call, we will see the following on a call providing a name as a parameter:

No alt text provided for this image

And the following on a call without providing the name as a parameter:

No alt text provided for this image

And there we have it! We have no successfully Injected a Service using constructor injection and best practice! Please do leave feedback and a reaction if you found this useful or enjoyable! In my next article, I will look to showing the wireframe from the first article in a microservice pattern.

Until next time!

Declan

Cristinel Marcu

Senior Software Developer

3 年

Very clear follow up for the first article. I would like to see also some unit tests!? What is your favourite framework for testing?

要查看或添加评论,请登录

Declan Taggart的更多文章

  • Bicep - Parameter Decorators

    Bicep - Parameter Decorators

    In my previous article, I covered Parameters and Variables and how to use them. This article is a continuation of my…

  • Bicep - Parameters and Variables

    Bicep - Parameters and Variables

    In my previous article, I covered the creation of a basic Bicep file with the commands that are required and a…

  • Create your first Bicep file

    Create your first Bicep file

    What is Bicep? Bicep is a domain-specific language (DSL) that uses a declarative syntax to deploy Azure Resources. In a…

  • Basic unit testing with xUnit

    Basic unit testing with xUnit

    Unit testing has many benefits and will help you identify bugs and problem areas in your code. If we look at the…

  • My basic wireframe for Azure Function v3

    My basic wireframe for Azure Function v3

    In this article, I will show you the wireframe I use for every Azure Function that I create and use. This demo will be…

    2 条评论

社区洞察

其他会员也浏览了