CD Smith
CD Smith

Reputation: 6607

Dependency Injection with Interface implemented by multiple classes

Update: Is there a way to achieve what I'm trying to do in an IoC framework other than Windsor? Windsor will handle the controllers fine but won't resolve anything else. I'm sure it's my fault but I'm following the tutorial verbatim and objects are not resolving with ctor injection, they are still null despite doing the registers and resolves. I've since scrapped my DI code and have manual injection for now because the project is time sensitive. Hoping to get DI worked out before deadline.


I have a solution that has multiple classes that all implement the same interface

As a simple example, the Interface

public interface IMyInterface {
    string GetString();
    int GetInt();
   ...
}

The concrete classes

public class MyClassOne : IMyInterface {
    public string GetString() {
        ....
    }
    public int GetInt() {
        ....
    }
}

public class MyClassTwo : IMyInterface {
    public string GetString() {
        ....
    }
    public int GetInt() {
        ....
    }
}

Now these classes will be injected where needed into layers above them like:

public class HomeController {

    private readonly IMyInterface myInterface;

    public HomeController() {}

    public HomeController(IMyInterface _myInterface) {
        myInterface = _myInterface
    }
    ...
}

public class OtherController {

    private readonly IMyInterface myInterface;

    public OtherController() {}

    public OtherController(IMyInterface _myInterface) {
        myInterface = _myInterface
    }
    ...
}

Both controllers are getting injected with the same interface.

When it comes to resolving these interfaces with the proper concrete class in my IoC, how do I differentiate that HomeController needs an instance of MyClassOne and OtherController needs an instance of MyClassTwo?

How do I bind two different concrete classes to the same interface in the IoC? I don't want to create 2 different interfaces as that breaks the DRY rule and doesn't make sense anyway.

In Castle Windsor I would have 2 lines like this:

container.Register(Component.For<IMyInterface>().ImplementedBy<MyClassOne>());
container.Register(Component.For<IMyInterface>().ImplementedBy<MyClassTwo>());

This won't work because I will only ever get a copy of MyClassTwo because it's the last one registered for the interface.

Like I said, I don't get how I can do it without creating specific interfaces for each concrete, doing that breaks not only DRY rules but basic OOP as well. How do I achieve this?


Update based on Mark Polsen's answer


Here is my current IoC, where would the .Resolve statements go? I don' see anything in the Windsor docs

public class Dependency : IDependency {

    private readonly WindsorContainer container = new WindsorContainer();

    private IDependency() {
    }

    public IDependency AddWeb() {
        ...

        container.Register(Component.For<IListItemRepository>().ImplementedBy<ProgramTypeRepository>().Named("ProgramTypeList"));
        container.Register(Component.For<IListItemRepository>().ImplementedBy<IndexTypeRepository>().Named("IndexTypeList"));

        return this;
    }

    public static IDependency Start() {
        return new IDependency();
    }
}

Upvotes: 9

Views: 5788

Answers (3)

VivekDev
VivekDev

Reputation: 25389

Typically DI containers follow Register, Resolve and Release patterns. During the register phase there are two steps. The first is to specify the mapping as you are doing. The second step is to specify the rules which govern which to inject where.

This problem is very common when we try to address Cross cutting concerns using decorators. In these situations, you have multiple classes(decorators) implementing a single interface.

Briefly, we need to implement IModelInterceptorsSelector which allows you to write imperative code that decides which Interceptor to apply to which types or members.

This is elaborately described in the book Dependency Injection in .Net book by Mark Seemann. Look for chapter 9 interception or search for the above interface.

I am not an expert at this, but was searching for the exact same problem and found the ans in the above book.

Hope this helps.

Regards Dev1

Upvotes: 2

Mark Polsen
Mark Polsen

Reputation: 149

You should be able to accomplish it with named component registration.

container.Register(Component.For<IMyInterface>().ImplementedBy<MyClassOne>().Named("One"));
container.Register(Component.For<IMyInterface>().ImplementedBy<MyClassTwo>().Named("Two"));

and then resolve them with

kernel.Resolve<IMyInterface>("One");

or

kernel.Resolve<IMyInterface>("Two");

See: To specify a name for the component

Upvotes: 5

VJAI
VJAI

Reputation: 32758

I hope you can use service overrides.

Ex.

container.Register(
    Component.For<IMyService>()
        .ImplementedBy<MyServiceImpl>()
        .Named("myservice.default"),
    Component.For<IMyService>()
        .ImplementedBy<OtherServiceImpl>()
        .Named("myservice.alternative"),

    Component.For<ProductController>()
        .ServiceOverrides(ServiceOverride.ForKey("myService").Eq("myservice.alternative"))
);

public class ProductController
{
    // Will get a OtherServiceImpl for myService.
    // MyServiceImpl would be given without the service override.
    public ProductController(IMyService myService)
    {
    }
}

Upvotes: 7

Related Questions