Search code examples
dependency-injectionasp.net-web-api2inversion-of-controldryioc

How is a DryIoc container setup if you need different instances of same interface?


I am trying to use DryIoc with a .NET Web API 2 site to create my controllers. I have a situation where my controller needs a processor and the processor needs two instances of a storage class. Here is the basics:

public interface IStorage
{
    IEnumerable<string> List();
    void Add(string file);
    void Remove(string file);
}

public class FileSystemStorage : IStorage
{
    // Implement to store on file system.
}

public class S3Storage : IStorage
{
    // Implement to store in S3 bucket.
}

public interface IProcessor
{ 
    void Process();
}

public class Processor(IStorage sourceStorage, IStorage targetStorage)
{ // Implement a process that interacts with both storages }

public class ProcessController : ApiController
{
    private readonly IProcessor processor;
    public ProcessController(IProcessor processor)
    {
        this.processor = processor;
    }
}

So, I need to have my IOC container (DryIoc) use two different classes for the interface IStorage. So, what I'd like is to setup IOC for something like this:

var sourceStorage = new FileSystemStorage();
var targetStorage = new S3Storage();
var processor = new Processor(sourceStorage, targetStorage);
// And then have DryIoc dependency resolver create 
// controller with this processor.

However, the normal way to register just won't work:

var c = new Container().WithWebApi(config);

// Need two different implementations...
c.Register<IStorage, ???>();

// And even if I had two instances, how would 
// the processor know which one to use for what parameter?
c.Register<IProcessor, Processor>();

I'm new to dependency injection containers and most of the docs are very abstract; I'm not grokking them. How is this done?


Solution

  • Direct way is identify registrations of different storage implementations with different keys, and instruct processor what is what:

    c.Register<IStorage, Foo>(serviceKey: "in");
    c.Register<IStorage, Bar>(serviceKey: "out");
    c.Register<IProcessor, Processor>(made: Parameters.Of
        .Name("source", serviceKey: "in")
        .Name("target", serviceKey: "out"));
    

    Problem, that it is a fragile approach cause changing parameter names will break the setup.

    May be, you need to review why you have two identical interface params with different responsibilities, and distinguish their roles with more fitting absractions/interfaces.