Using Constructor Injection to Implement DI in Microsoft Azure

Using Constructor Injection to Implement DI in Microsoft Azure

Before we look into the implementation of DI in Azure functions, we shall look into what dependency injection is and its advantages.

What is Dependency Injection (DI)?

Dependency Injection is a design pattern that helps achieve loosely coupled codes. Dependency Injection is a technique used for application development on Azure Cloud to achieve Inversion of Control (IoC) between classes and their dependencies.

What are the Advantages of Dependency Injection?

For effective cloud application development, It is required to make our code more maintainable, reusable, more readable, scalable and testable. DI makes unit testing easier as we can mock dependencies. So, using DI we can achieve all these things and in this article, we will look at how to implement Azure functions.

Azure Functions is a server-less offering from Microsoft Azure (similar to Lambda function in AWS) to concentrate on solving business problems rather than maintaining infrastructure.

To understand the DI, We will implement a dependency injection (DI) pattern in Azure functions v2 and create a simple HTTP trigger function that downloads the blob data from the Azure blob storage (We will use an Azure storage emulator for blob storage).

Getting started with Cloud Application Development and implementing DI in Microsoft Azure

Let us create a new Azure function project using Visual Studio 2019 (IDE) as we cannot implement dependency injection using the Azure portal.

New project

After selecting the Azure functions as shown in the above screenshot, you can click on the Next button to configure a new project with a name. 

configure new project

Let’s name the project “MyFunctionApp”. You can give any relevant name to your project. After entering the project name and other details you can click on the Next button to create the HTTP trigger as below screenshot.

new azure functions

As we are going to create Azure functions v2, we have to select the option and then select Http Trigger as shown in the above screenshot.

Now, the configuration has been done and once we click on the “Create“button it will create a sample HTTP trigger function with code files as below screenshot.

solution explorer

Now, we are ready to implement the DI in our Azure function and to do that, we need to create an interface and a class that has an implementation of our interface method(s).

IStorageService.cs

using System; 
using System.Threading.Tasks; 
 
namespace MyFunctionApp 
{ 
    public interface IStorageService 
	{ 
        Task<Byte[]> DownloadBlob(string containerName, string file); 
	} 
} 

StorageService.cs

using Microsoft.WindowsAzure.Storage; 
using Microsoft.WindowsAzure.Storage.Blob; 
using System; 
using System.Threading.Tasks; 
 
namespace MyFunctionApp 
{ 
    public class StorageService: IStorageService 
	{ 
        private readonly CloudBlobClient blobClient; 
 
        public StorageService(string storageConnectionString) 
    	{ 
            var mycloudStorageAccount = CloudStorageAccount.Parse(storageConnectionString); 
            blobClient = mycloudStorageAccount.CreateCloudBlobClient(); 
    	} 
 
        public async Task<Byte[]> DownloadBlob(string containerName, string file) 
    	{ 
            CloudBlobContainer container = blobClient.GetContainerReference(containerName); 
            CloudBlockBlob blockBlob = container.GetBlockBlobReference(file); 
 
            await blockBlob.FetchAttributesAsync(); 
            long fileByteLength = blockBlob.Properties.Length; 
            byte[] fileContent = new byte[fileByteLength]; 
            for (int i = 0; i < fileByteLength; i++) 
        	{ 
                fileContent[i] = 0x20; 
        	} 
            await blockBlob.DownloadToByteArrayAsync(fileContent, 0); 
            return fileContent; 
    	} 
	} 
} 

We have now created the IStorageService interface and StorageService class as shown in the code samples above. Now, we need to create a Startup assembly file that initiates the build object to register the StorageService object with the IStorageService interface.

We will need to include below nuget package:

  • Microsoft.Azure.Functions.Extensions 1.1.0

After installing the mentioned package in the project, we can create a Startup class to register our service with service lifetime in the root of the project. You should have the below code sample to register the service with IoC container. Here we provide a delegate to manually instantiate the storage service.

Startup.cs

using Microsoft.Azure.Functions.Extensions.DependencyInjection; 
using Microsoft.Extensions.DependencyInjection; 
using MyFunctionApp; 
 
[assembly: FunctionsStartup(typeof(Startup))] 
namespace MyFunctionApp 
{ 
    public class Startup : FunctionsStartup 
	{ 
        private static string storageConnectionString = System.Environment.GetEnvironmentVariable("AzureWebJobsStorage", System.EnvironmentVariableTarget.Process); 
        public override void Configure(IFunctionsHostBuilder builder) 
    	{ 
            builder.Services.AddSingleton<IStorageService>(provider => new StorageService(storageConnectionString)); 
 
            builder.Services.AddLogging(); 
    	} 
	} 
} 

Service Lifetimes

In the above code sample, I have registered a service with the AddSingleton method to create an object of service once in a service lifetime.

You can register our service with 3 different service lifetime methods:

  • AddSingleton – objects are the same for every object and every request.
  • AddScoped – objects are the same within a request, but different across different requests.
  • AddTransient – objects are always different; a new instance is provided to every controller and every service.

Now, we can inject our service in the constructor to the HttpTrigger function and use its method to download the blob data from the Azure blob storage. In this case, we have used an Azure storage emulator to download blob data.

Function1.cs

using System.Threading.Tasks; 
using Microsoft.AspNetCore.Mvc; 
using Microsoft.Azure.WebJobs; 
using Microsoft.Azure.WebJobs.Extensions.Http; 
using Microsoft.AspNetCore.Http; 
using Microsoft.Extensions.Logging; 
 
namespace MyFunctionApp 
{ 
    public class Function1 
	{ 
        private readonly IStorageService _storageService; 
        public Function1(IStorageService storageService) 
    	{ 
            _storageService = storageService; 
    	} 
 
    	[FunctionName("DownloadBlob")] 
        public 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 containerName = req.Query["containerName"]; 
            string fileName = req.Query["fileName"]; 
 
            byte[] downloadedBlob = await _storageService.DownloadBlob(containerName, fileName); 
 
            return new OkObjectResult(downloadedBlob); 
    	} 
	} 
} 

When we create an azure function with a default template it creates a function class with a static keyword. 

If you look at the above Azure function code, I have removed the static keyword before the class name. Because we need to add a constructor of the class to inject our service dependency, registered in the service builder class in the Startup.cs file. Note: Static classes cannot have constructors. 

We have declared a read-only private property for the IStorageService Interface in the Constructor that will initiate the StorageService in the runtime. The IoC container will inject the correct implementation of the interface based on the setup in the Startup class.

Now our code is ready to test!

Azure storage emulator:

Azure storage emulator

Results

You can build and run the project. In my case, It runs with the below URL to download the blob from Azure blob storage.

http://localhost:7071/api/DownloadBlob?containerName=profile-images&fileName=azure-developer-associate.png

In this request to download the blob data, I have passed the containerName and fileName parameters as a query string that we want to download. 

Here is the actual request and response in the Postman:

response in the Postman

That’s it for dependency injection in Azure functions using C#. In case of any doubts, do not hesitate to get in touch with an expert at DEV IT here.