Search code examples
c#dependency-injectionservice-provider

Get the correct service from DI


I got a class using C# and .NET 6:

public class MyClass()
{
   private IMyInterface _myInterface;

   public MyClass(IMyInterface interfaceInstance)
   {
      _myInterface = interfaceInstance;
   }
   ...
}

I have 2 different classes which implements IMyInterface called ClassA and ClassB.

I want to be able to make a DI twice - once for MyClass which receives ClassA as the instance in the ctr and once for ClassB:

public void ConfigureServices(IServiceCollection services)
{
   services.AddSingleton<MyClass>(sp => new MyClass(new ClassA()));
   services.AddSingleton<MyClass>(sp => new MyClass(new ClassB()));
}

The problem is that I don't know how to later get a specific one of them using a ServiceProvider


Solution

  • So in .net 6 you can't do this directly.

    So you need to either register your classes as using their own dedicated interfaces, or enumerate over them when injecting.

    So to enumerate you do this.

    public class MyClass(IEnumerable<IMyInterface > services)
    {
       ...
    }
    

    But you still need a way to distinguish between each instance, which may or may not be possible depending on your structure.

    So the best way is to make each Class dependent on a specific interface. The interface can simply be empty.

    So you have

    public interface IMyInterface 
    {
       string MyProp { get; set; }
       ...
    }
    
    public interface IMyInterfaceA: IMyInterface {} //Will be used by ClassA only
    public interface IMyInterfaceB: IMyInterface {}  //Will be used by ClassB only
    

    Then you have

    services.AddSingleton<IMyInterfaceA, ClassA>();  
    services.AddSingleton<IMyInterfaceB, ClassB>();  
    

    If you switch to .Net8 then you can use the new KeyServices option.

    services.AddKeyedSingleton<IMyInterface , ClassA>("KeyNameClass_A");
    services.AddKeyedSingleton<IMyInterface , ClassB>("KeyNameClass_B");
    

    Then you can inject it like this

    public class MyClass([FromKeyedServices("KeyNameClass_A")] IMyInterface classA)
    {
     ...
    }
    

    (and the same for ClassB)