Azure Function V3 configuration with DI

Ershad Nozari 426 Reputation points
2021-12-10T23:44:31.793+00:00

I have a Azure Function with 2 triggers:

namespace FunctionApp
{
    public class Functions
    {
        private readonly IService service;

        public Functions(IService  service)
        {
            this.service = service;
        }

        [FunctionName("Function1")]
        public void Function1([BlobTrigger("funcone-requests/{name}")]Stream myBlob, string name)
        {
            this.service.DoWork();        }

        [FunctionName("Function2")]
        public void Function2([BlobTrigger("functwo-requests/{name}")] Stream myBlob, string name)
        {
            this.service.DoWork();
        }
    }
}

I’m registering IService in my Startup class like so:

[assembly: FunctionsStartup(typeof(FunctionApp.Startup))]
namespace FunctionApp
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            builder.Services.AddScoped<IService, Service>();
        }
    }
}

I need to inject a different configuration in the Service class depending on which trigger that is calling DoWork(). How can I achieve this using DI?

public interface IService
{
    void DoWork();
}

public class Service : IService
{
    public Service(/*Configuration to be injected depending on calling trigger */)
    { }

    public void DoWork()
    { }
}

Configuration extract:

public class Config
{
    public string Server { get; set; }
    public string Port { get; set; }
}

{
  "Values": {
    "Function1:Server": "Function1_Server",
    "Function1:Port": "Function1_Port",
    "Function2:Server": "Function2_Server",
    "Function2:Port": "Function2_Port"
  }
}
Azure Functions
Azure Functions
An Azure service that provides an event-driven serverless compute platform.
5,908 questions
0 comments No comments
{count} votes

Accepted answer
  1. Jaliya Udagedara 2,836 Reputation points MVP Volunteer Moderator
    2021-12-11T18:53:05.333+00:00

    An alternate way would be,

    Create different Services,

    public interface IServiceBase
    {
        public void DoWork();
    }
    
    public abstract class ServiceBase : IServiceBase
    {
        private readonly Config _config = new();
    
        // you can choose what to get, may be instead of this, you can do, 
        // ServiceBase(IConfiguration configuration, string functionName) 
        // It's upto you
        public ServiceBase(IConfigurationSection configurationSection)
        {
            configurationSection.Bind(_config);
        }
    
        public virtual void DoWork()
        {
            // TODO
        }
    }
    
    public interface IService1 : IServiceBase { }
    
    public class Service1 : ServiceBase, IService1
    {
        public Service1(IConfiguration configuration) : base(configuration.GetSection("Function1")) { }
    }
    
    public interface IService2 : IServiceBase { }
    
    public class Service2 : ServiceBase, IService2
    {
        public Service2(IConfiguration configuration) : base(configuration.GetSection("Function2")) { }
    }
    

    Split Functions class,

    public class Function1
    {
        private readonly IService1 service1;
    
        public Function1(IService1 service1)
        {
            this.service1 = service1;
        }
    
        [FunctionName("Function1")]
        public void Run([BlobTrigger("funcone-requests/{name}")] Stream myBlob, string name)
        {
            this.service1.DoWork();
        }
    }
    
    public class Function2
    {
        private readonly IService2 service2;
    
        public Function2(IService2 service2)
        {
            this.service2 = service2;
        }
    
        [FunctionName("Function2")]
        public void Run([BlobTrigger("functwo-requests/{name}")] Stream myBlob, string name)
        {
            this.service2.DoWork();
        }
    }
    

    And in the Startup,

    [assembly: FunctionsStartup(typeof(FunctionApp.Startup))]
    namespace FunctionApp
    {
        public class Startup : FunctionsStartup
        {
            public override void Configure(IFunctionsHostBuilder builder)
            {
                builder.Services.AddScoped<IService1, Service1>();
                builder.Services.AddScoped<IService2, Service2>();
            }
        }
    }
    
    1 person found this answer helpful.

1 additional answer

Sort by: Most helpful
  1. Jaliya Udagedara 2,836 Reputation points MVP Volunteer Moderator
    2021-12-11T03:29:56.68+00:00

    First of all, thank you for providing a nice code sample.

    You have the 2 functions in one class. In that case, I would suggest something like below,

    Update IService to expose a method to retrieve Config by FunctionName. And DoWork method to accept Config.

    public interface IService
    {
        Config GetConfig(string functionName);
    
        public void DoWork(Config config);
    }
    
    public class Service : IService
    {
        private readonly IConfiguration _configuration;
    
        public Service(IConfiguration configuration)
        {
            _configuration = configuration;
        }
    
        public Config GetConfig(string functionName)
        {
            return new Config
            {
                Port = _configuration.GetValue<string>($"{functionName}:Port"),
                Server = _configuration.GetValue<string>($"{functionName}:Server")
            };
        }
    
        public void DoWork(Config config)
        {
            // TODO
        }
    }
    

    Now from the Functions class, get the Config based on the function and pass it to DoWork

    public class Functions
    {
        private readonly IService service;
    
        public Functions(IService service)
        {
            this.service = service;
        }
    
        [FunctionName("Function1")]
        public void Function1([BlobTrigger("funcone-requests/{name}")] Stream myBlob, string name, ExecutionContext executionContext)
        {
            DoWork(executionContext.FunctionName);
        }
    
        [FunctionName("Function2")]
        public void Function2([BlobTrigger("functwo-requests/{name}")] Stream myBlob, string name, ExecutionContext executionContext)
        {
            DoWork(executionContext.FunctionName);
        }
    
        private void DoWork(string functionName)
        {
            Config config = this.service.GetConfig(functionName);
            this.service.DoWork(config);
        }
    }
    

    Haven't personally tried out the code. But you get the concept.

    1 person found this answer helpful.

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.