Dependency Injection for Azure Function

Enabling dependency injection on your Azure Function a little bit different than the usual process like you do on the .Net Core applications. Here I'm planning to share some tips and steps you should follow if you need to enable DI on Azure Functions with C#.

I'm focusing on a function application created for HTTP Trigger. When you create a new project you will see a static class and method to run your function end-point. 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
   public static class FunctionWithDI
   {
        [FunctionName("function-with-di")]
        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.");
            return new OkObjectResult("C# HTTP trigger function processed a request.");
        }
    }

First, you need to install "Microsoft.Azure.Functions.Extensions" NuGet package to your project. This package helps to enable DI on function apps. So, you can add a new class to startup your project which you can declare all the dependency registrations. 

In my case, I create "Startup.cs" class in my project. Then you need to inherit the Startup class from "FunctionsStartup" which comes with Microsoft.Azure.Functions.Extensions.DependencyInjection library. You can implement the abstract Configure method to register your dependencies. 

1
2
3
4
5
6
7
8
9
10
11
12
   using System;
   using Microsoft.Azure.Functions.Extensions.DependencyInjection; 
   namespace FunctionWithDI
   {
       public class Startup : FunctionsStartup
       {
           public override void Configure(IFunctionsHostBuilder builder)
           {
               throw new NotImplementedException();
           }
       }
   }
   

Now, in this sample application, I'm showing you how you inject Serilog logger and sample service to your function. You can install the latest Serilog NuGet package along with the below dependencies. 

- Serilog
- Serilog.Extensions.Logging
- Serilog.Sinks.Console
- Serilog.Sinks.File

Update your Configure() method like below.

1
2
3
4
5
6
7
8
  public override void Configure(IFunctionsHostBuilder builder)
  {
     var logger = new LoggerConfiguration()
                      .WriteTo.Console()
                      .WriteTo.File("log.txt", rollingInterval: RollingInterval.Day)
                      .CreateLogger();
     builder.Services.AddLogging(lb => lb.AddSerilog(logger));
  }
    

Then I'm going to add a sample service interface and service class to calculate the sum of two numbers.

 Service interface :

1
2
3
4
 public interface IService
 {
   double CalculateSum(double one, double two);
 } 
    

Service implementation :

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 public class Service : IService
 {
    private ILogger<Service> _logger;
    public Service(ILogger<Service> logger)
    {
      _logger = logger;
    }
    public double CalculateSum(double one, double two)
    {
      _logger.LogInformation($"calculating value {one} and {two}");
      return one + two;
    }
 }
   

Then finally you need to register this service in your Startup class like below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 public override void Configure(IFunctionsHostBuilder builder)
 {
    var logger = new LoggerConfiguration()
                   .WriteTo.Console()
                   .WriteTo.File("log.txt", rollingInterval: RollingInterval.Day)
                   .CreateLogger();
   
    builder.Services.AddLogging(lb => lb.AddSerilog(logger));
   
    builder.Services.AddSingleton<IService, Service>();
 }
   

Once you have all these changes in place you must include the below decorator/attribute on the Startup class to force it as the Function Startup. This is a very important step you shouldn't skip. 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
[assembly: FunctionsStartup(typeof(Startup))]
namespace FunctionWithDI
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            var logger = new LoggerConfiguration()
                .WriteTo.Console()
                .WriteTo.File("log.txt", rollingInterval: RollingInterval.Day)
                .CreateLogger();
            builder.Services.AddLogging(lb => lb.AddSerilog(logger));
            builder
                .Services
                .AddSingleton<IService, Service>();
        }
    }
} 
After this point you have everything set up for Startup class, so let's move to the next phase. You will need to get rid of static implementation on the function class and make it looks like below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 public class FunctionWithDI
 {
    private IService _service;
   
    public FunctionWithDI(IService service)
    {
      _service = service;
    }
   
    [FunctionName("function-with-di")]
    public async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
    ILogger log)
    {
       double one = double.Parse(req.Query["one"]);
       double two = double.Parse(req.Query["two"]);
   
       log.LogInformation("Calculate method is invoked");
   
       return new OkObjectResult(_service.CalculateSum(one, two));
    }
 } 
   

Once you have all these changes in your project you should be able to run your Azure Function application with dependency injection support. 

Hope this will help you to code smarter. 

Use this source code in github as a reference.

Cheers!

Comments

Popular posts from this blog

How to enable Remote Desktop on Azure Cloud Service

C# Interactive