Search code examples
c#dependency-injectionunity-containersolid-principles

About SOLID principles, DI using containers and Unity: How to manage the DI mapping in the container?


Allright. I finally understood all those complicated concepts of DI even IoC, containers, and so on and on. But there is something I'm missing still.

Say that I have a class called SomeClass that will instaciate in his constructor one of the classes that implememnts IFirstLevelOfAbstraction. The classes that implements IFirstLevelOfAbstraction are SubClass1 and SubClass2. Those classes I mentioned instanciate a class implementing ISecondLevelOfAbstraction, which are SubClass3 and SubClass4.

TL;DR here is the image.

enter image description here

Using unity in my entry point this should look something like this:

IUnityContainer container = new UnityContainer();
container.RegisterType<SomeClass>();
container.RegisterType<IFirstLevelOfAbstraction, SubClass1>();
container.RegisterType<IFirstLevelOfAbstraction, SubClass2>();
container.RegisterType<ISecondLevelOfAbstraction, SubClass3>();
container.RegisterType<ISecondLevelOfAbstraction, SubClass4>();
var someClass= container.Resolve<SomeClass>();

Question Being: How do I choose which path will the DI have in the container?

i.e The instance of IFirstLevelOfAbstraction being SubClass1 and for ISecondLevelOfAbstraction being SubClass4.

How do I easily change this later on?

Having so many DI nested isn't an anti-pattern? Why? Why is this any good?

I feel like nobody uses this. It's not even native in C#.


Solution

  • One way to do it is not to register SubClass1, SubClass2,SubClass3, or SubClass4 and then choose such dependencies when you resolve. Here is an example:

    IUnityContainer container = new UnityContainer();
    
    var someClass = container.Resolve<SomeClass>(
        new DependencyOverride<IFirstLevelOfAbstraction>(new ResolvedParameter<SubClass1>()),
        new DependencyOverride<ISecondLevelOfAbstraction>(new ResolvedParameter<SubClass4>()));
    

    If this is not an option for you (you don't want to specify anything at resolve time), you need to use named registrations. I wouldn't recommend going that route though.

    Since you have multiple implementations for a single interface, I recommend that you use Pure DI. See my article here for a reason why.

    Here is how your code would look like with Pure DI:

    var someClass =
        new SomeClass(
            new SubClass1(
                new SubClass4()));