2

I am referring to this link that explains how to implement DI in Azure functions. I want to implement a Factory pattern where my Factory class should be able to resolve other dependencies while constructing the factory object.

Here is the code.

public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {

        System.Console.WriteLine("***************************I am in startup....");
        builder.Services.AddScoped<PayloadProcessorFactory>();
        
        /*  I tried to explicitly add IFunctionsHostBuilder to Services but its not working.
            builder.Services.AddScoped<IFunctionsHostBuilder>((s) =>{
            System.Console.WriteLine("test");                
            return builder;
        });*/

        /*No problem with below two dependencies and are getting injected without any issue*/
        builder.Services
        .AddScoped<IPayloadProcessor,DoctorPayloadProcessor>()
        .AddScoped<DoctorPayloadProcessor>();

        builder.Services
        .AddScoped<IPayloadProcessor,PatientPayloadProcessor>()
        .AddScoped<PatientPayloadProcessor>();
    }
}

Here is my factory class

public class PayloadProcessorFactory
{
    private readonly IFunctionsHostBuilder builder;
    private readonly ILogger _logger;
    public PayloadProcessorFactory(IFunctionsHostBuilder builder,ILogger<PayloadProcessorFactory> logger)
    {
        _logger = logger;
       this.builder = builder; //Builder is always null
       _logger.LogDebug("Constructing PayloadProcesorFactory");
    }
}

In the above code value of builder is always null hence I can't resolve dependencies using following statement

payloadProcessor = builder.Services.BuildServiceProvider()
                .GetRequiredService<DoctorPayloadProcessor>();

In Startup.cs I tried to explicitly add IFunctionsHostBuilder to the service collection but after that, I ran into below issue.

Exception has occurred: CLR/System.InvalidOperationException
An exception of type 'System.InvalidOperationException' occurred in Microsoft.Extensions.DependencyInjection.dll but was not handled in user code: 'Unable to resolve service for type 'Microsoft.Azure.WebJobs.Script.IEnvironment' while attempting to activate 'Microsoft.Azure.WebJobs.Script.Configuration.ScriptHostOptionsSetup'.'

QUESTION How can I inject IServiceProvider to my factory class in order to build a complex objects and resolve multiple dependencies?

Project details

  • Dotnet framework - netcoreapp2.1
  • Azure functions version : 2.0
user2243747
  • 2,767
  • 6
  • 41
  • 61

2 Answers2

1

Here's an example of the factory patter with DI in .NET Core. You don't need to inject the service provider to resolve the classes.

Sample factory interface

public interface IFactory
{
    IWorker Create(WorkerType workerType);
}

Sample factory implementation

public class Factory: IFactory
{
    private readonly IEnumerable<IWorker> _workers;

    public AuthenticationEngineFactory(IEnumerable<IWorker> workers)
    {
        _workers = workers;
    }

    public IWorker Create(WorkerType workerType)
    {
        var worker = _workers.FirstOrDefault(x => x.WorkerType == workerType);
        if (worker is null) throw new System.ArgumentException($"Worker with type '{workerType}' is not supported.");
        return worker;
    }
}

Sample worker interface

public interface IWorker
{
    WorkerType WorkerType { get; } // This can be an enum.
    void DoWork();
}

Sample worker implementations.

public class Worker1Implementation : IWorker
{
     public WorkerType WorkerType => WorkerType.WorkerType1;

     public void DoWork() {}; // Add implementation
}

public class Worker2Implementation : IWorker
{
     public WorkerType WorkerType => WorkerType.WorkerType2;

     public void DoWork() {}; // Add implementation
}

In your functions Startup.cs

builder.Services.AddSingleton<IFactory, Factory>();
builder.Services.AddScoped<IWorker, Worker1Implementation>();
builder.Services.AddScoped<IWorker, Worker2Implementation>();
lopezbertoni
  • 3,551
  • 3
  • 37
  • 53
0

Not sure if the answer to this question is still relevant, but I would approach this task the following way. I'd only register two implementations of IPayloadProcessor and a factory and then use the factory in a consumer of IPayloadProcessor (I named it PayloadProcessorConsumer in my example) to get the needed type based on some parameter (in my example I'm using a string but it could very well be a bool or an enum).

So in terms of your initial example it would look like:

Startup.cs

public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        builder.Services.AddScoped<PayloadProcessorFactory>();
        builder.Services.AddScoped<DoctorPayloadProcessor>();
        builder.Services.AddScoped<PatientPayloadProcessor>();
        builder.Services.AddScoped<PayloadProcessorConsumer>();
    }
}

Please keep in mind that you won't need to register IServiceProvider in the IoC container, it's there by default and it allows you to resolve the registered services in the factory itself.

PayloadProcessorFactory.cs

public class PayloadProcessorFactory
{
    private readonly IServiceProvider _serviceProvider;
    private readonly ILogger<PayloadProcessorFactory> _logger;

    public PayloadProcessorFactory(IServiceProvider serviceProvider, ILogger<PayloadProcessorFactory> logger)
    {
        _logger = logger;
        _serviceProvider = serviceProvider;
    }

    public IPayloadProcessor Create(string processorName) // I'd use enum or bool here
    {
        if (processorName == "Doctor")
            return _serviceProvider.GetRequiredService<DoctorPayloadProcessor>();
        else
            return _serviceProvider.GetRequiredService<PatientPayloadProcessor>();
    }
}

Then in the class which should be consumer of a particular IPayloadProcessor the factory is injected and the required processor is instantiated at runtime.

PayloadProcessorConsumer.cs

public class PayloadProcessorConsumer
{
    private readonly PayloadProcessorFactory _payloadProcessorFactory;

    public PayloadProcessorConsumer(PayloadProcessorFactory payloadProcessorFactory)
    {
        _payloadProcessorFactory = payloadProcessorFactory;
    }

    public void DoWork()
    {
        var processorName = "Doctor"; // TODO some logic to retrieve required processor name from configuration

        IPayloadProcessor payloadProcessor = _payloadProcessorFactory.Create(processorName);

        // TODO: work to do with IPayloadProcessor
    }
}

Obviously, I'm oversimplifying the example and avoiding introducing interface for the factory itself etc., but I believe it's enough to illustrate the idea.

There is also a pretty detailed article in Microsoft Docs on a topic which is close to this one.

Stas Ivanov
  • 1,173
  • 1
  • 14
  • 24