How to register multiple implementations of the same interface in Asp.Net Core?
Asked Answered
A

42

580

I have services that are derived from the same interface.

public interface IService { }
public class ServiceA : IService { }
public class ServiceB : IService { } 
public class ServiceC : IService { }

Typically, other IoC containers like Unity allow you to register concrete implementations by some Key that distinguishes them.

In ASP.NET Core, how do I register these services and resolve them at runtime based on some key?

I don't see any Add Service methods that take a key or name parameter, which would typically be used to distinguish the concrete implementation.

    public void ConfigureServices(IServiceCollection services)
    {            
         // How do I register services of the same interface?            
    }


    public MyController:Controller
    {
       public void DoSomething(string key)
       { 
          // How do I resolve the service by key?
       }
    }

Is the Factory pattern the only option here?

Update1
I have gone though the article here that shows how to use the factory pattern to get service instances when we have multiple concrete implementations. However, it is still not a complete solution. When I call the _serviceProvider.GetService() method, I cannot inject data into the constructor.

For example consider this:

public class ServiceA : IService
{
     private string _efConnectionString;
     ServiceA(string efconnectionString)
     {
       _efConnecttionString = efConnectionString;
     } 
}

public class ServiceB : IService
{    
   private string _mongoConnectionString;
   public ServiceB(string mongoConnectionString)
   {
      _mongoConnectionString = mongoConnectionString;
   }
}

public class ServiceC : IService
{    
    private string _someOtherConnectionString
    public ServiceC(string someOtherConnectionString)
    {
      _someOtherConnectionString = someOtherConnectionString;
    }
}

How can _serviceProvider.GetService() inject the appropriate connection string? In Unity, or any other IoC library, we can do that at type registration. I can use IOption, however, that will require me to inject all settings. I cannot inject a particular connection string into the service.

Also note that I am trying to avoid using other containers (including Unity) because then I have to register everything else (e.g., Controllers) with the new container as well.

Also, using the factory pattern to create service instances is against DIP, as it increases the number of dependencies a client has details here.

So, I think the default DI in ASP.NET Core is missing two things:

  1. The ability to register instances using a key
  2. The ability to inject static data into constructors during registration
Astrophotography answered 26/8, 2016 at 21:42 Comment(5)
Possible duplicate of Dependency injection resolving by nameJeb
Can Update1 be moved to a different question as injecting things in constructors is very different from working out which object to constructIntreat
Future readers may want to look at my answer here (#42402564) to avoid..what I would say is .. introducing service-locator into the mix. Just giving another option.Justly
The problem here is the requirement of a key. If we remove the notion of the key, we can have our factory and eat it too. The problem here is business logic we are forcing down into implementation with the standard factory pattern idea (forcing everything to have a key). The volatility is in the business logic, not the implementation. If we consider that as our volatile thing that needs to be abstracted, needing a key goes away. Please check my answer below for implementation details. AMA.Diphenylamine
With .NET 8 Microsoft has brought an important update to the extension libraries which brings keyed DI services so this is now supported out of the box.Saga
Q
470

I did a simple workaround using Func when I found myself in this situation.

Firstly declare a shared delegate:

public delegate IService ServiceResolver(string key);

Then in your Startup.cs, setup the multiple concrete registrations and a manual mapping of those types:

services.AddTransient<ServiceA>();
services.AddTransient<ServiceB>();
services.AddTransient<ServiceC>();

services.AddTransient<ServiceResolver>(serviceProvider => key =>
{
    switch (key)
    {
        case "A":
            return serviceProvider.GetService<ServiceA>();
        case "B":
            return serviceProvider.GetService<ServiceB>();
        case "C":
            return serviceProvider.GetService<ServiceC>();
        default:
            throw new KeyNotFoundException(); // or maybe return null, up to you
    }
});

And use it from any class registered with DI:

public class Consumer
{
    private readonly IService _aService;

    public Consumer(ServiceResolver serviceAccessor)
    {
        _aService = serviceAccessor("A");
    }

    public void UseServiceA()
    {
        _aService.DoTheThing();
    }
}

Keep in mind that in this example the key for resolution is a string, for the sake of simplicity and because OP was asking for this case in particular.

But you could use any custom resolution type as key, as you do not usually want a huge n-case switch rotting your code. Depends on how your app scales.

Questionable answered 25/5, 2017 at 10:14 Comment(22)
How do you retrieve one of the services by string from another class?Runofthemill
@MatthewStevenMonkan updated my answer with an exampleQuestionable
Using a factory pattern like this is the best way to go. Thanks for sharing!Agapanthus
+1 Very neat and clean, because when we use other di-container we have to include their package whenever we need to resolve dependencies, eg. ILifetimeScope in AutoFac.Fortyfour
@AnupamSingh In my opinion, most kind of small to medium applications running on .NET Core do not need any DI framework, just adds complexity and unwanted dependencies, the beauty and simplicity of the built-in DI is more than enough, and it can also be extended with ease.Questionable
While it seems @Miguel A. Arilla has point it out clearly, I created on top of his useful solution another solution which looks neat but requires alot more work. It definitely depends on the above solution.Davit
+1 but I recommend using descriptions as keys. Example: "legacy" for ServiceA, "modern" for ServiceB, "next" for ServiceC. That makes it easier to remove or replace service registrations without having to update all the code that calls the Func.Bedfordshire
i did the same thing but throwing exception Activated Event Time Duration Thread Exception thrown: 'System.ObjectDisposedException' in Microsoft.Extensions.DependencyInjection.dll ("Cannot access a disposed object.") Exception thrown: 'System.ObjectDisposedException' in Microsoft.Extensions.DependencyInjection.dll ("Cannot access a disposed object.") Hyperlink: Activate Historical Debugging 8.97s [428] WorkPool-Session#2:Connection(3a5aaef7-e40f-4607-9d12-eacbd0dd5f6d,amqp://localhost:5672) Panegyrize
Down vote explanation - Its very interesting but I am currently refactoring a massive code base to remove all this Func magic somebody did a few years ago (before the MS DI revolution) The problem with this is that it dramatically increases the connascence complexity on properties which can cause convoluted DI resolution further down the line. For example I worked on a Windows service handler had over 1.6k lines of code to do with Func and after doing it the recommended way of DI I reduced it to 0.2k lines. OK-Lines of code mean nothing.. except its easier to read and resuse now...Derbent
@MiguelA.Arilla: Can you please explain how to create an object of type "Consumer" or how to do the dependency injection of that class?Fairleigh
@MiguelA.Arilla It looks like this approach is not working in .net core 3.1 . Below is my sample code and value of fly object is always null ->> var services = new ServiceCollection(); services.AddTransient<IFly,Bird>(); services.AddTransient<IFly,Aeroplane>(); var sp = services.BuildServiceProvider(); var fly = sp.GetService<Aeroplane>();//Not able to resolve Aeroplane. Always null fly.Fly();Antigorite
I understand this is a workaround. It is not exactly an answer, so I can't up-vote. I won't down-vote because there is a clear notice that this is a workaround. If we are counting on dependency injection to isolate clients of interfaces from knowledge of implementations, this didn't work. The client c'tor has to make a choice.Kyat
@SkipSaillors Yes the Controller has to make a choice because the DI container will not allow specifying what service you want to inject.Sharpen
@Sharpen The ASP.NET Core DI container doesn't allow you to inject the service you want in a straightforward manner, true. Core DI does simple injection simply. Complicated injection scenarios are tricky using Microsoft DI. Other DI frameworks offer other methods to support tricky injections.Kyat
Using typeof(Class) operator you can also create Dictionary<string, Type> types and then use serviceProvider.GetService(types[key]) in order to avoid the switch.Humboldt
Down vote explanation - I agree with @PiotrKula and have a full implementation in my answer below. We can remove the notion of the key in a very easy way. Check my answer below.Diphenylamine
@Antigorite your fly resolution is always null b/c you're asking the provider for a TService of Aeroplane, but the only TService your provider only knows of is IFly. In your example Aeroplane is a TImplementation. If you want your implementation to be registered as a service you should use .AddTransient<Aeroplane>(). Then to use the service locator pattern shown in this answer to diff between those implementations on some key.Inspectorate
I disagree with the premise of a consumer locating a dependency by magic string. A consumer that must know about the implementation (here abstracted by magic string) is the opposite of Dependency Inversion. If the consumer will "break" if it doesn't get the right implementation then you're not really using the strategy pattern. Register those implementations as individual TService interfaces. Try not to reuse TService when registering unless your using patterns like the chain of responsibility where the consumer can handle ALL the items in IEnumerable<TService>.Inspectorate
@PiotrKula I think this is still useful with Microsoft's current dependency injection framework. Consider this: You're implementing the command pattern with multiple command classes. Users can run commands. Their requests are stored in a database table, including the command's type and arguments. Now you're writing code to execute user requests. The code reads the request from the database, then needs to resolve it to a concrete command class. How would you wire that up without a Func or an equivalent class?Marjoriemarjory
@EricEskildsen I still would not use Func<> directly like that even today. What I would do is something along the line of MetaProgramming (Reflection) and look at type matching or expression matching. In your case though I would apply the Actor Pattern - So you can register multiple concrete commands under the ICommand interface and then your processor works out which command/s from the injected IEnumerable<ICommand> to run - I am literally doing (something more or less) like that now. 😀😅 Either way no Func'y business needed!Derbent
@PiotrKula Ah, so what you're objecting to is the answer's suggested Func implementation with switch more than Func itself (or let's just say any function), yes? Because using reflection is still writing a factory that you'll need Func or a class for. There's just no switch. (I say "just," but I agree it's an improvement with regard to open/closed past a certain complexity.) At first I interpreted your comment as, "There's a built-in way to do this with the MS DI framework that eliminates the need for a custom function completely," so that's what I was curious about.Marjoriemarjory
The answer should be updated with the new .NET8 keyed dependency injection learn.microsoft.com/en-us/aspnet/core/fundamentals/…Zenia
H
170

DISCLAIMER: this is not a good solutions for all fits. I reach it to solve a specific problem. in the comments below you can read and evaluate the drawbacks of this approach.


Another option is to use the extension method GetServices from Microsoft.Extensions.DependencyInjection.

Register your services as:

services.AddSingleton<IService, ServiceA>();
services.AddSingleton<IService, ServiceB>();
services.AddSingleton<IService, ServiceC>();

Then resolve with a little of Linq:

var services = serviceProvider.GetServices<IService>();
var serviceB = services.First(o => o.GetType() == typeof(ServiceB));

or

var serviceZ = services.First(o => o.Name.Equals("Z"));

(assuming that IService has a string property called "Name")

Make sure to have using Microsoft.Extensions.DependencyInjection;

Update

AspNet 2.1 source: GetServices

Harakiri answered 3/7, 2017 at 18:22 Comment(16)
In your code, if I resolved only one service, not services, which one will be returned? The service I registered first or late?Farnesol
Not sure, but I think it isn't deterministic. Any results you get today may change tomorrow, it seems not a good practice.Harakiri
Pretty nice solution. Should be on top.Kimbro
upvote to the link for GetServices, which showed me that you can request a list of services a dependent service by requesting IEnumerable<IService>Uptake
Mohammed Noureldin - Good comment. It seems like you need to get all of them and choose which one to use. It would be nice to have way of adding many implementations and making choice automatic depending on specific class. ie. class Duck : IFly. class HoneyBird : IFly and then they would get different concrete implementations depending on which bird implements them. Otherwise it would require iterating through them or some other gymnastics.Ornstead
serviceProvider.GetServices<IService>() will instantiate each of ServiceA, ServiceB and ServiceC. You would like to call constructor of only one service - the one that you actually need. This is a big problem if implementations are not light weight or you have many implementations of IService (for example, you have auto-generated implementations of IRepository for each model).Ungainly
I agree with @Uros. This is not a good solution. Imagine what happens if you register 10 IService-implementations and the instance you actually need is the last one. In this case, 9 instances are actually created by DI, which are never used.Abdulabdulla
If I understand it correctly, you'll be injecting the ServiceProvide into the object and then using that to require the correct implementation. In this way, you are no longer doing Dependency Injection, instead, following the Service Locator pattern, which is considered a antipattern.Geriatrics
You can inject different implementations (as sown above) of the interface and then in the constructor (where DI is needed) you can use IEnumarable<IService> services - Then when needed you can iterate services to select the none/one/many services you want to use. NB - Be careful because this constructor DI pattern allows for NO dependencies to be available, almost like optional dependency. Its much better than using Func but both solutions are S.O.L.I.D anti-patterns so probably worth checking if your design isn't leaking abstractionsDerbent
Bad idea: Multiple unused instances, service locator anti pattern and direct coupling to the actual implementation (typeof<ServiceA>).Rowlett
I do not see an anti-pattern per se, if you have different implemantions to the same problem (and more may be added later) and some factors at run time determine which one to use, you can get the collection of service and than filter for the one that matches - we used similar patterns in JAVA with Spring and OSGiSalliesallow
This is a bad solution. it will instantiate all thos servicesBaize
Good or bad? Depends. I have a use case where multiple implementations of the interface is to be used.Iraq
I can't believe this and the top answer got so many votes. Both of these rely on ServiceLocator. While I understand the draw to refactor a massive codebase and the simplicity this and the other answer brings, the best answer I've seen is this one: https://mcmap.net/q/73164/-how-to-register-multiple-implementations-of-the-same-interface-in-asp-net-core. This answer is better because it lends itself to a more SOLID way of thinking, and doesn't introduce another framework.Jodijodie
If you know what you need ask for the concrete one. If you need to get a named instance just select it and when it is slow defer initialization (e.g. wrap them in a proxy with a lazy<IService>.) (personally I think slow constructors are a design problem, like calling a database they should always be fast as there is no way to call them asynchronously.)Abdicate
For me this was good in scenario when I needed to call all services with same inherited interfaces: custom validation checks on certain object. I didn't want to go with some kind of factory pattern and this approach is nice as all I need is to just register new validator without coupling with another code.Henrion
N
90

A factory approach is certainly viable. Another approach is to use inheritance to create individual interfaces that inherit from IService, implement the inherited interfaces in your IService implementations, and register the inherited interfaces rather than the base. Whether adding an inheritance hierarchy or factories is the "right" pattern all depends on who you speak to. I often have to use this pattern when dealing with multiple database providers in the same application that uses a generic, such as IRepository<T>, as the foundation for data access.

Example interfaces and implementations:

public interface IService 
{
}

public interface IServiceA: IService
{}

public interface IServiceB: IService
{}

public interface IServiceC: IService
{}

public class ServiceA: IServiceA 
{}

public class ServiceB: IServiceB
{}

public class ServiceC: IServiceC
{}

Container:

container.Register<IServiceA, ServiceA>();
container.Register<IServiceB, ServiceB>();
container.Register<IServiceC, ServiceC>();
Nameplate answered 22/1, 2018 at 18:9 Comment(2)
this doesn't help injection when I need to inject the most generic type (IService) - but DIFFERENT implementations for different use casesTeresetereshkova
What if you want to get all 3 IService's?Outcry
I
67

I just simply inject an IEnumerable

ConfigureServices in Startup.cs

Assembly.GetEntryAssembly().GetTypesAssignableFrom<IService>().ForEach((t)=>
                {
                    services.AddScoped(typeof(IService), t);
                });

Services Folder

public interface IService
{
    string Name { get; set; }
}

public class ServiceA : IService
{
    public string Name { get { return "A"; } }
}

public class ServiceB : IService
{    
    public string Name { get { return "B"; } }
}

public class ServiceC : IService
{    
    public string Name { get { return "C"; } }
}

MyController.cs

public class MyController
{
    private readonly IEnumerable<IService> _services;
    public MyController(IEnumerable<IService> services)
    {
        _services = services;
    }
    public void DoSomething()
    {
        var service = _services.Where(s => s.Name == "A").Single();
    }
...
}

Extensions.cs

    public static List<Type> GetTypesAssignableFrom<T>(this Assembly assembly)
    {
        return assembly.GetTypesAssignableFrom(typeof(T));
    }
    public static List<Type> GetTypesAssignableFrom(this Assembly assembly, Type compareType)
    {
        List<Type> ret = new List<Type>();
        foreach (var type in assembly.DefinedTypes)
        {
            if (compareType.IsAssignableFrom(type) && compareType != type)
            {
                ret.Add(type);
            }
        }
        return ret;
    }
Impenetrable answered 31/8, 2018 at 21:4 Comment(8)
In the DoSomething() method of the Controller you can use typeof to resolve the service you want: var service = _services.FirstOrDefault(t => t.GetType() == typeof(ServiceA));Observable
I literally tried everything, and this is the only solution which worked for me. Thanks!Grounder
@Grounder Try the solution I created below in another post. I think it is cleaner and simpler to use.Impenetrable
This is good--i was trying to inject a List, and it didn't work. i has to be an enumerable.Calcareous
thank you for storing that variant, works great for my case, also I like that more then variant below, especially for the Assembly.GetEntryAssembly().GetTypesAssignableFrom<IService>().ForEach((t)=> { services.AddScoped(typeof(IService), t); });Sandbag
This is not a proper solution, as it creates instance of every service (that implements IService in case of this example) at the moment IEnumerable<IService> is injected. If services have their own dependencies, then the list of objects created may be very large. In other words you'll be creating instances of objects you don't even use, which will waste memory and put pressure on GC.Trisyllable
@Trisyllable I agree from a factory standpoint but there could be a need to inject all types for an interface if you want to cycle through all and execute a specific method.Impenetrable
@TBrown Yes chain-of-responsibility vs strategy pattern, but in most cases I use strategy, and only had to use chain only few times.Trisyllable
C
62

Bit late to this party, but here is my solution:...

Startup.cs or Program.cs if Generic Handler...

services.AddTransient<IMyInterface<CustomerSavedConsumer>, CustomerSavedConsumer>();
services.AddTransient<IMyInterface<ManagerSavedConsumer>, ManagerSavedConsumer>();

IMyInterface of T Interface Setup

public interface IMyInterface<T> where T : class, IMyInterface<T>
{
    Task Consume();
}

Concrete implementations of IMyInterface of T

public class CustomerSavedConsumer: IMyInterface<CustomerSavedConsumer>
{
    public async Task Consume();
}

public class ManagerSavedConsumer: IMyInterface<ManagerSavedConsumer>
{
    public async Task Consume();
}

Accessing the services in a controller

public class MyController
{
    private readonly IMyInterface<CustomerSavedConsumer> _customerSavedConsumer;
    private readonly IMyInterface<ManagerSavedConsumer> _managerSavedConsumer;

    public MyController(IMyInterface<CustomerSavedConsumer> customerSavedConsumer, IMyInterface<ManagerSavedConsumer> managerSavedConsumer)
    {
        _customerSavedConsumer = customerSavedConsumer;
        _managerSavedConsumer = managerSavedConsumer;
    }
}

Hopefully if there is any issue with doing it this way, someone will kindly point out why this is the wrong way to do this.

Czar answered 24/9, 2018 at 13:20 Comment(17)
IMyInterface<CustomerSavedConsumer> and IMyInterface<ManagerSavedConsumer> are different service types - this does not answer OPs question at all.Deplete
The OP wanted a way of registering multiple implementations of the same interface in Asp.net core. If i didnt do this, please explain how (exactly).Czar
Your types are different because the generic argument is different.Deplete
While you are correct, this pattern allows the effect that the op wanted. At least when I was trying to do this myself I stumbled across this post and my solution worked best for my situation.Czar
I expect the issue was more that registering multiple implementations for a single interface (using MS DI) doesn’t allow for the container to distinguish one implementation from another. In other DIs you can key them so the container knows which to choose. In MS you have to use a delegate and choose manually. Your solution does not address this scenario as your interfaces are different, so the container has no issue picking the right implementation. While your sample obviously works, it’s not a solution for the problem as stated.Deplete
Hi Richard, Would you go as far as to write a full suggestion on this thread to illustrate the correct method you’ve said. I would like to see your solution. Kindest regards, GrayCzar
There are already several fine solutions presented, so it would be redundant to restate them, but for the record I use factory with a dictionary in its constructor. That uses a delegate for instantiation so the types are registered. The down side is that I need a delegate registration everywhere I want to use one of the services, but this is just a limitation of the MS DI system. I normally sub in DryIoc if it’s appropriate to do so, as it has many additional features, including registered instances and keyed primitives.Deplete
@Czar Even though your post got some bad press, I thank you for putting this solution forward. It gives readers another option to overcome the limitations in .net cores DI. Although it may not answer the OPs question directly, it provides a perfect alternative solution, which is what SO is all about, right?Circumscribe
@Czar SO is about sharing solutions that solve the problem presented in the questions, and you did. Your solution is how I have always solved this problem. This answers the heart of the problem this question is attempting to solve in a cleaner and simpler way. This way there are multiple options to choose from to solve the same problem, which is what software engineering is all about.Aday
@Czar This resolved the issue was having for how to handle multiple concrete implementations for an interface and perform the DI. Thanks for the solution.Sitdown
@Czar thanks for posting this answer. I have been wondering about this for a little while and this answer is probably the best one I've seen here so far, even though some were complaining about the solution, this certainly is the best one so far.Jodijodie
I ran into the issue myself and worked out that this was the simplest approach. No nasty switch statement nor key. Way to go!!!Falsework
This is the best... Full C# and OOP and KISS. Leave string keys to Javascripters, we have fully featured type system. And if we need to be more flexible, there is always reflection available.Humboldt
@Czar This is an elegant and straightforward solution that is easy to follow and works as advertised. No delegate registrations, no keys, just pure DI. Works perfectly in ASP.NET Core 3.1 and 6. Well done, and thank you.Tenorite
Most elegant solution without hairs.Budding
One suggestion: a none-generic base interface should define Consume(). IMyInterface<T> then inherits IMyInterface. With that you can use the generic interface only for DI and the none-generic interface everywhere else.Budding
Thank you for the amazing answer. I'm facing one issue though. I have declared a property/variable in CustomerSavedConsumer class. But I'm not able to access it through _customerSavedConsumer since it's the type of IMyInterface<CustomerSavedConsumer> & that interface don't have that member.Martini
R
35

Most of the answers here violate the single responsibility principle (a service class should not resolve dependencies itself) and/or use the service locator anti-pattern.

Another option to avoid these problems is to:

  • use an additional generic type parameter on the interface or a new interface implementing the non generic interface,
  • implement an adapter/interceptor class to add the marker type and then
  • use the generic type as “name”

I’ve written an article with more details: Dependency Injection in .NET: A way to work around missing named registrations

Rowlett answered 3/8, 2019 at 18:37 Comment(4)
how does accepted answer violets the single responsibility principle?Astrophotography
See comments of https://mcmap.net/q/73164/-how-to-register-multiple-implementations-of-the-same-interface-in-asp-net-core and also in the accepted answer the service is resolved lazyily, ie you only know if it fails at runtime and there is no way to statically check this on startup after container build (similar to the answer in the comment). SRP because the service is not only responsible for its business logic but also for dependency resolutionRowlett
@RicoSuter I really like the solution in your blog, but am confused by your DI within the Startup class. Specificaly, I do not understand the line MessagePublisher("MyOrderCreatedQueue") since I do not see a constructor with that signature. services.AddSingleton<IMessagePublisher<OrderCreatedMessage>>( new MessagePublisher<OrderCreatedMessage>( new MessagePublisher("MyOrderCreatedQueue")));Sitdown
Thanks, updated the article and use MyMessagePublisher as an sample implementation of IMessagePublisherRowlett
M
24

It is not supported by Microsoft.Extensions.DependencyInjection.

But you can plug-in another dependency injection mechanism, like StructureMap See it's Home page and it's GitHub Project.

It's not hard at all:

  1. Add a dependency to StructureMap in your project.json:

    "Structuremap.Microsoft.DependencyInjection" : "1.0.1",
    
  2. Inject it into the ASP.NET pipeline inside ConfigureServices and register your classes (see docs)

    public IServiceProvider ConfigureServices(IServiceCollection services) // returns IServiceProvider !
    {
        // Add framework services.
        services.AddMvc();
        services.AddWhatever();
    
        //using StructureMap;
        var container = new Container();
        container.Configure(config =>
        {
            // Register stuff in container, using the StructureMap APIs...
            config.For<IPet>().Add(new Cat("CatA")).Named("A");
            config.For<IPet>().Add(new Cat("CatB")).Named("B");
            config.For<IPet>().Use("A"); // Optionally set a default
            config.Populate(services);
        });
    
        return container.GetInstance<IServiceProvider>();
    }
    
  3. Then, to get a named instance, you will need to request the IContainer

    public class HomeController : Controller
    {
        public HomeController(IContainer injectedContainer)
        {
            var myPet = injectedContainer.GetInstance<IPet>("B");
            string name = myPet.Name; // Returns "CatB"
    

That's it.

For the example to build, you need

    public interface IPet
    {
        string Name { get; set; }
    }

    public class Cat : IPet
    {
        public Cat(string name)
        {
            Name = name;
        }

        public string Name {get; set; }
    }
Marrowbone answered 26/8, 2016 at 23:59 Comment(8)
I've tried this approach, but I get runtime errors on my controller because IContainer is not found in the build plans. Is there anything I must to to require IContainer to be auto-injected?Overburden
BTW, I'm using StructureMap.Micorosoft.DependencyInjection 1.3.0.Overburden
Are you returning the new container in ConfigureServices?Marrowbone
I'm returning the new container's IServiceProviderInstance as indicated in step #2 above. I copied that exactly only changing it for my types. This is a good solution and is working perfectly. The only drawback is that I'm unable to use an injected container and am resorting to a static container, which I don't want to do.Overburden
Its works for me thanks GerardoGrignoli. @mohrtan the sample code is here if you are still looking into this. github.com/Yawarmurtaza/AspNetCoreStructureMapVioletvioleta
@GerardoGrignoli its works great but soon i came across the issue of resolving "new Cat("CatA")" when registering it against IPet interface.Violetvioleta
Injecting the containter into your controller defeats the whole purpose of IoC imho.Railroad
Too heavy to introduce another container for this simple feature with lots of questions of using it in comments.Overflight
C
21

Why not use inheritance? This way we can have as many copies of the interface as we want and we can pick suitable names for each of them . And we have a benefit of type safety

public interface IReportGenerator
public interface IExcelReportGenerator : IReportGenerator
public interface IPdfReportGenerator : IReportGenerator

Concrete classes:

public class ExcelReportGenerator : IExcelReportGenerator
public class PdfReportGenerator : IPdfReportGenerator

Register:

instead of

services.AddScoped<IReportGenerator, PdfReportGenerator>();
services.AddScoped<IReportGenerator, ExcelReportGenerator>();

we have :

services.AddScoped<IPdfReportGenerator, PdfReportGenerator>();
services.AddScoped<IExcelReportGenerator, ExcelReportGenerator>();

Client:

public class ReportManager : IReportManager
{
    private readonly IExcelReportGenerator excelReportGenerator;
    private readonly IPdfReportGenerator pdfReportGenerator;

    public ReportManager(IExcelReportGenerator excelReportGenerator, 
                         IPdfReportGenerator pdfReportGenerator)
    {
        this.excelReportGenerator = excelReportGenerator;
        this.pdfReportGenerator = pdfReportGenerator;
    }

this approach also allows for louse coupled code, because we can move IReportGenerator to the core of the application and have child interfaces that will be declared at higher levels.

Conquistador answered 31/10, 2020 at 22:4 Comment(1)
I use this approach regularly, but when you want to register plugins from an external assembly or similar, you would have multiple same interfaces. +1 for your own code base, -1 for external libraries ;)Shala
S
17

>= .NET 8: Keyed DI services

With .NET 8 we get an update of the extension libraries which brings keyed DI services which is exactly what you need.

Here the Microsoft documentation quoted from What's new in .NET 8? - Keyed DI services:


Keyed DI services

Keyed dependency injection (DI) services provides a means for registering and retrieving DI services using keys. By using keys, you can scope how your register and consume services. These are some of the new APIs:

The following example shows you to use keyed DI services.

using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton<BigCacheConsumer>();
builder.Services.Addsingleton<SmallCacheConsumer>();

builder.Services.AddKeyedSingleton<IMemoryCache, BigCache>("big");
builder.Services.AddKeyedSingleton<IMemoryCache, SmallCache>("small");

var app = builder.Build();

app.MapGet("/big", (BigCacheConsumer data) => data.GetData());
app.MapGet("/small", (SmallCacheConsumer data) => data.GetData());

app.Run();

class BigCacheConsumer([FromKeyedServices("big")] IMemoryCache cache)
{
    public object? GetData() => cache.Get("data");
}

class SmallCacheConsumer(IKeyedServiceProvider keyedServiceProvider)
{
    public object? GetData() => keyedServiceProvider.GetRequiredKeyedService<IMemoryCache>("small");
}

For more information, see dotnet/runtime#64427.


Saga answered 30/8, 2023 at 12:11 Comment(0)
K
16

You're correct, the built in ASP.NET Core container does not have the concept of registering multiple services and then retrieving a specific one, as you suggest, a factory is the only real solution in that case.

Alternatively, you could switch to a third party container like Unity or StructureMap that does provide the solution you need (documented here: https://docs.asp.net/en/latest/fundamentals/dependency-injection.html?#replacing-the-default-services-container).

Kaine answered 26/8, 2016 at 23:43 Comment(1)
I think the way Jason Roberts proposes in his post Injecting a Factory Service in ASP.NET Core could be a nice extension for factory approach in this case - see ServiceCollectionExtensions.AddFactory example in the post.Characterization
S
15

Necromancing.
I think people here are reinventing the wheel - and badly, if I may say so ...
If you want to register a component by key, just use a dictionary:

System.Collections.Generic.Dictionary<string, IConnectionFactory> dict = 
    new System.Collections.Generic.Dictionary<string, IConnectionFactory>(
        System.StringComparer.OrdinalIgnoreCase);

dict.Add("ReadDB", new ConnectionFactory("connectionString1"));
dict.Add("WriteDB", new ConnectionFactory("connectionString2"));
dict.Add("TestDB", new ConnectionFactory("connectionString3"));
dict.Add("Analytics", new ConnectionFactory("connectionString4"));
dict.Add("LogDB", new ConnectionFactory("connectionString5"));

And then register the dictionary with the service-collection:

services.AddSingleton<System.Collections.Generic.Dictionary<string, IConnectionFactory>>(dict);

if you then are unwilling to get the dictionary and access it by key, you can hide the dictionary by adding an additional key-lookup-method to the service-collection:
(the use of delegate/closure should give a prospective maintainer a chance at understanding what's going on - the arrow-notation is a bit cryptic)

services.AddTransient<Func<string, IConnectionFactory>>(
    delegate (IServiceProvider sp)
    {
        return
            delegate (string key)
            {
                System.Collections.Generic.Dictionary<string, IConnectionFactory> dbs = Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService
 <System.Collections.Generic.Dictionary<string, IConnectionFactory>>(sp);

                if (dbs.ContainsKey(key))
                    return dbs[key];

                throw new System.Collections.Generic.KeyNotFoundException(key); // or maybe return null, up to you
            };
    });

Now you can access your types with either

IConnectionFactory logDB = Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService<Func<string, IConnectionFactory>>(serviceProvider)("LogDB");
logDB.Connection

or

System.Collections.Generic.Dictionary<string, IConnectionFactory> dbs = Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService<System.Collections.Generic.Dictionary<string, IConnectionFactory>>(serviceProvider);
dbs["logDB"].Connection

As we can see, the first one is just completely superfluous, because you can also do exactly that with a dictionary, without requiring closures and AddTransient (and if you use VB, not even the braces will be different):

IConnectionFactory logDB = Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService<System.Collections.Generic.Dictionary<string, IConnectionFactory>>(serviceProvider)["logDB"];
logDB.Connection

(simpler is better - you might want to use it as extension method though)

Of course, if you don't like the dictionary, you can also outfit your interface with a property Name (or whatever), and look that up by key:

services.AddSingleton<IConnectionFactory>(new ConnectionFactory("ReadDB"));
services.AddSingleton<IConnectionFactory>(new ConnectionFactory("WriteDB"));
services.AddSingleton<IConnectionFactory>(new ConnectionFactory("TestDB"));
services.AddSingleton<IConnectionFactory>(new ConnectionFactory("Analytics"));
services.AddSingleton<IConnectionFactory>(new ConnectionFactory("LogDB"));



// https://mcmap.net/q/73164/-how-to-register-multiple-implementations-of-the-same-interface-in-asp-net-core
services.AddTransient<Func<string, IConnectionFactory>>(
    delegate(IServiceProvider sp)
    {
        return
            delegate(string key)
            {
                System.Collections.Generic.IEnumerable<IConnectionFactory> svs = 
                    sp.GetServices<IConnectionFactory>();
                
                foreach (IConnectionFactory thisService in svs)
                {
                    if (key.Equals(thisService.Name, StringComparison.OrdinalIgnoreCase))
                        return thisService;
                }
    
                return null;
            };
    });

But that requires changing your interface to accommodate the property, and looping through a lot of elements should be much slower than an associative-array lookup (dictionary).
It's nice to know that it can be done without dictionary, though.

These are just my $0.05

Spelling answered 24/4, 2019 at 7:53 Comment(4)
If service has IDispose implemented, who is responsible for disposing the service? You have registered dictionary as SingletonAstrophotography
@LP13: You could also register dictionary with a delegate as value, then you can register it in itransient, and create a new instance,eg. GetRequiredService<T>()["logDB"]()Spelling
I tried your stuff with dictionary, the problem is: it only open one connection for all. It's like a static any queries that want to be executed will use the same connection. And the connection can be already in use.Ernestineernesto
The solution would be Dictionary<Database, Func<IDbConnection>> I will put my answer at the bottom of this postErnestineernesto
I
15

since my post above, I have moved to a Generic Factory Class

Usage

 services.AddFactory<IProcessor, string>()
         .Add<ProcessorA>("A")
         .Add<ProcessorB>("B");

 public MyClass(IFactory<IProcessor, string> processorFactory)
 {
       var x = "A"; //some runtime variable to select which object to create
       var processor = processorFactory.Create(x);
 }

Implementation

public class FactoryBuilder<I, P> where I : class
{
    private readonly IServiceCollection _services;
    private readonly FactoryTypes<I, P> _factoryTypes;
    public FactoryBuilder(IServiceCollection services)
    {
        _services = services;
        _factoryTypes = new FactoryTypes<I, P>();
    }
    public FactoryBuilder<I, P> Add<T>(P p)
        where T : class, I
    {
        _factoryTypes.ServiceList.Add(p, typeof(T));

        _services.AddSingleton(_factoryTypes);
        _services.AddTransient<T>();
        return this;
    }
}
public class FactoryTypes<I, P> where I : class
{
    public Dictionary<P, Type> ServiceList { get; set; } = new Dictionary<P, Type>();
}

public interface IFactory<I, P>
{
    I Create(P p);
}

public class Factory<I, P> : IFactory<I, P> where I : class
{
    private readonly IServiceProvider _serviceProvider;
    private readonly FactoryTypes<I, P> _factoryTypes;
    public Factory(IServiceProvider serviceProvider, FactoryTypes<I, P> factoryTypes)
    {
        _serviceProvider = serviceProvider;
        _factoryTypes = factoryTypes;
    }

    public I Create(P p)
    {
        return (I)_serviceProvider.GetService(_factoryTypes.ServiceList[p]);
    }
}

Extension

namespace Microsoft.Extensions.DependencyInjection
{
    public static class DependencyExtensions
    {
        public static FactoryBuilder<I, P> AddFactory<I, P>(this IServiceCollection services)
            where I : class
        {
            services.AddTransient<IFactory<I, P>, Factory<I, P>>();
            return new FactoryBuilder<I, P>(services);
        }
    }
}
Impenetrable answered 14/12, 2019 at 20:3 Comment(6)
Can you provide .AddFactory() method extention?Alcohol
Sorry Just saw this...addedImpenetrable
The AddFactory extension takes a delegate. Your usage doesn't work because there is none.Jurgen
_services.AddSingleton(_factoryTypes); I feel like this line should be in the FactoryBuilder constructor, otherwise you will call it every time when you call add.Jurgen
you are correct. Not sure where that came from. I have updated the code.Impenetrable
Thanks for this answer, it works well. I post a code sample here for anyone who may need it. github.com/wswind/ServiceMultiImplEvelunn
C
13

I've faced the same issue and want to share how I solved it and why.

As you mentioned there are two problems. The first:

In Asp.Net Core how do I register these services and resolve it at runtime based on some key?

So what options do we have? Folks suggest two:

  • Use a custom factory (like _myFactory.GetServiceByKey(key))

  • Use another DI engine (like _unityContainer.Resolve<IService>(key))

Is the Factory pattern the only option here?

In fact both options are factories because each IoC Container is also a factory (highly configurable and complicated though). And it seems to me that other options are also variations of the Factory pattern.

So what option is better then? Here I agree with @Sock who suggested using custom factory, and that is why.

First, I always try to avoid adding new dependencies when they are not really needed. So I agree with you in this point. Moreover, using two DI frameworks is worse than creating custom factory abstraction. In the second case you have to add new package dependency (like Unity) but depending on a new factory interface is less evil here. The main idea of ASP.NET Core DI, I believe, is simplicity. It maintains a minimal set of features following KISS principle. If you need some extra feature then DIY or use a corresponding Plungin that implements desired feature (Open Closed Principle).

Secondly, often we need to inject many named dependencies for single service. In case of Unity you may have to specify names for constructor parameters (using InjectionConstructor). This registration uses reflection and some smart logic to guess arguments for the constructor. This also may lead to runtime errors if registration does not match the constructor arguments. From the other hand, when using your own factory you have full control of how to provide the constructor parameters. It's more readable and it's resolved at compile-time. KISS principle again.

The second problem:

How can _serviceProvider.GetService() inject appropriate connection string?

First, I agree with you that depending on new things like IOptions (and therefore on package Microsoft.Extensions.Options.ConfigurationExtensions) is not a good idea. I've seen some discussing about IOptions where there were different opinions about its benifit. Again, I try to avoid adding new dependencies when they are not really needed. Is it really needed? I think no. Otherwise each implementation would have to depend on it without any clear need coming from that implementation (for me it looks like violation of ISP, where I agree with you too). This is also true about depending on the factory but in this case it can be avoided.

The ASP.NET Core DI provides a very nice overload for that purpose:

var mongoConnection = //...
var efConnection = //...
var otherConnection = //...
services.AddTransient<IMyFactory>(
             s => new MyFactoryImpl(
                 mongoConnection, efConnection, otherConnection, 
                 s.GetService<ISomeDependency1>(), s.GetService<ISomeDependency2>())));
Cima answered 31/8, 2016 at 22:10 Comment(8)
Hi, sorry for my stupid question, but I'm about new with Microsoft.Extensions.DependencyInjection ... do you think that create 3 interfaces that extend Iservice like "public interface IServiceA : IService" and than "public class ServiceA : IServiceA" ... could be a good practice option?Llywellyn
Using another DI engine is not evil even in Microsoft's opinion.That's why they have created the DI framework extensible. Trying to achieve what they've already done is reinventing the wheel. Autofac, Unity, StructureMap, all these are mature DI frameworks and I'd definitely use one of them. But of course,I'd use a small abstraction layer to minimize my direct dependencies to any of those.Trouper
@emiliano-magliocca In general, you should not depend on interfaces that you dont use (ISP), IServiceA in your case. Since you're using methods from IService only, you should have dependency to IService only.Cima
@cagatay-kalan In case of OP's question he can easily achieve his goal with ASP.NET Core DI. No need for other DI frameworks.Cima
@Cima thanks for your reply, let's try to be more specific,I have a print interface, and two classes one that prints on a custom usbpos printer and one that print on a standard escpos printer, now I can have two different scenarios: 1- I use escpos in a class and I use usbpos in another class, that's where I planned to use to different interfaces and inject the proper one via constructor 2- I use the same interface, but I choose which printer to use via a "switch" Can you kindly tell me which approach would you use for this 2 scenarios separately ? plus if both scenario at same time?Llywellyn
@EmilianoMagliocca It can be easily solved this way: services.AddTransient<MyFirstClass>( s => new MyFirstClass(s.GetService<Escpos>())); for the first class and services.AddTransient<MySecondClass>( s => new MySecondClass(s.GetService<Usbpos>())); for the second one.Cima
@Cima thanks again, so my issue probably was the way I thought about DI ... you're saying that I can use also classes and not only interfaces, so it wouldn't be a bad practice to use a class instead of an interface? sorry if looks like a dumb question, but this would help me a lot to clarify all my doubts :)Llywellyn
@EmilianoMagliocca in my example both ‘MyFirstClass‘ and ‘MySecondClass‘ have the same ctor parameter of interface type which both Escpos and Usbpos implement. So code above only instructs IoC container how to instanciate ‘MyFirstClass‘ and ‘MySecondClass‘. Nothing more. So in addition you may need to map some other interface(s) to ‘MyFirstClass‘ and ‘MySecondClass‘. It depends on your needs and I didn't covered it in my example.Cima
C
13

Here is an example on how to create a Dependency Resolver that allows you to specify an generic argument to resolve your dependency.


var serviceProvider = new ServiceCollection()
    .AddSingleton<IPerson, Larry>()
    .AddSingleton<IPerson, Phil>()
    .AddSingleton<IDependencyResolver<IPerson, string>, PersonDependecyResolver>()
    .BuildServiceProvider();

var persons = serviceProvider.GetService<IDependencyResolver<IPerson, string>>();
Console.WriteLine(persons.GetDependency("Phil").GetName());

public interface IDependencyResolver<out TResolve, in TArg>
{
    TResolve GetDependency(TArg arg);
}

public class PersonDependecyResolver : IDependencyResolver<IPerson, string>
{
    private readonly IEnumerable<IPerson> people;

    public PersonDependecyResolver(IEnumerable<IPerson> people)
    {
        this.people = people;
    }
        
    public IPerson GetDependency(string arg)
    {
        return arg switch
        {
            "Larry" => this.people.FirstOrDefault(p => p.GetType() == typeof(Larry)),
            "Phil" => this.people.FirstOrDefault(p => p.GetType() == typeof(Phil)),
            _ => throw new Exception("Unable to resolve dependency")
        }
     
        ?? throw new Exception($"No type was found for argument {arg}");
    }
}
Chum answered 9/9, 2021 at 19:9 Comment(3)
this is the cleanest solutionPauperism
PersonDependecyResolver(IEnumerable<IPerson> people) this will create all the instances of IPersonEvelunn
@Evelunn this is correct for certain situations, I am assuming people are making the best judgment here and they understand how the service provider works. Inject IServiceProvider if you don't want the waste or are concerned about creating all instances, in most cases id say people want all instances to be created.Chum
L
11

Apparently, you can just inject IEnumerable of your service interface! And then find the instance that you want using LINQ.

My example is for the AWS SNS service but you can do the same for any injected service really.

Startup

foreach (string snsRegion in Configuration["SNSRegions"].Split(',', StringSplitOptions.RemoveEmptyEntries))
{
    services.AddAWSService<IAmazonSimpleNotificationService>(
        string.IsNullOrEmpty(snsRegion) ? null :
        new AWSOptions()
        {
            Region = RegionEndpoint.GetBySystemName(snsRegion)
        }
    );
}

services.AddSingleton<ISNSFactory, SNSFactory>();

services.Configure<SNSConfig>(Configuration);

SNSConfig

public class SNSConfig
{
    public string SNSDefaultRegion { get; set; }
    public string SNSSMSRegion { get; set; }
}

appsettings.json

  "SNSRegions": "ap-south-1,us-west-2",
  "SNSDefaultRegion": "ap-south-1",
  "SNSSMSRegion": "us-west-2",

SNS Factory

public class SNSFactory : ISNSFactory
{
    private readonly SNSConfig _snsConfig;
    private readonly IEnumerable<IAmazonSimpleNotificationService> _snsServices;

    public SNSFactory(
        IOptions<SNSConfig> snsConfig,
        IEnumerable<IAmazonSimpleNotificationService> snsServices
        )
    {
        _snsConfig = snsConfig.Value;
        _snsServices = snsServices;
    }

    public IAmazonSimpleNotificationService ForDefault()
    {
        return GetSNS(_snsConfig.SNSDefaultRegion);
    }

    public IAmazonSimpleNotificationService ForSMS()
    {
        return GetSNS(_snsConfig.SNSSMSRegion);
    }

    private IAmazonSimpleNotificationService GetSNS(string region)
    {
        return GetSNS(RegionEndpoint.GetBySystemName(region));
    }

    private IAmazonSimpleNotificationService GetSNS(RegionEndpoint region)
    {
        IAmazonSimpleNotificationService service = _snsServices.FirstOrDefault(sns => sns.Config.RegionEndpoint == region);

        if (service == null)
        {
            throw new Exception($"No SNS service registered for region: {region}");
        }

        return service;
    }
}

public interface ISNSFactory
{
    IAmazonSimpleNotificationService ForDefault();

    IAmazonSimpleNotificationService ForSMS();
}

Now you can get the SNS service for the region that you want in your custom service or controller

public class SmsSender : ISmsSender
{
    private readonly IAmazonSimpleNotificationService _sns;

    public SmsSender(ISNSFactory snsFactory)
    {
        _sns = snsFactory.ForSMS();
    }

    .......
 }

public class DeviceController : Controller
{
    private readonly IAmazonSimpleNotificationService _sns;

    public DeviceController(ISNSFactory snsFactory)
    {
        _sns = snsFactory.ForDefault();
    }

     .........
}
Louvenialouver answered 25/10, 2017 at 3:47 Comment(0)
D
10

I didn't have time to read through them all but it seemed everyone was providing solutions to problems that shouldn't exist in the first place.

If you need all of the registered IService implementations then you need them all. But DO NOT inject them all with IEnumerable and then use logic to select one based on some type of key. Problem with doing that is you need a key and the logic should not need to change if the key changes ie; different implementation of IService so typeof doesn't work any more.

The Real Problem

There is business logic here that should be in an engine service. Something like IServiceDecisionEngine is needed. The implementation of the IServiceDecisionEngine gets ONLY the needed IService implementations from DI. Like

public class ServiceDecisionEngine<SomeData>: IServiceDecisionEngine<T> 
{
    public ServiceDecisionEngine(IService serviceA, IService serviceB) { }

    public IService ResolveService(SomeData dataNeededForLogic)
    {
        if (dataNeededForLogic.someValue == true) 
        { 
            return serviceA;
        } 
        return serviceB;
    }
}

Now in your DI, you can do .AddScoped<IServiceDecisionEngine<SomeData>, new ServiceDecisionEngine(new ServiceA(), new ServiceB()) and the managerService that needs an IService will get it by injecting and using IServiceDecisionEngine.

Diphenylamine answered 13/2, 2021 at 20:45 Comment(0)
B
10

The best documentation/tutorial I found for multiple implementation are from this source: .NET Core Dependency Injection - One Interface, Multiple Implementations, (Authored by Akshay Patel)

Example mentioned in the tutorial follows the Controller/Service/Repository convention, with Func implementation in the ConfigurationService() from Startup.cs to instantiate the proper/needed interface implementation. Tutorial was the best recipe I found to clarify this issue.

Below, a crude copy/paste from the article mentioned above: (example deals with 3 different implementations of a shopping cart interface, one method with a cache solution, another with API and other implementation with DB.)
Interface to be multiple implemented....

namespace MultipleImplementation  
{  
    public interface IShoppingCart  
    {  
        object GetCart();  
    }  
}  


implementation A

namespace MultipleImplementation  
{  
    public class ShoppingCartCache : IShoppingCart  
    {  
        public object GetCart()  
        {  
            return "Cart loaded from cache.";  
        }  
    }  
}  


Implementation B

namespace MultipleImplementation  
{  
    public class ShoppingCartDB : IShoppingCart  
    {  
        public object GetCart()  
        {  
            return "Cart loaded from DB";  
        }  
    }  
}  


Implementation C

namespace MultipleImplementation  
{  
    public class ShoppingCartAPI : IShoppingCart  
    {  
        public object GetCart()  
        {  
            return "Cart loaded through API.";  
        }  
    }  
}  


An Interface Declaration in Repository to select rather A,B,C will be used....

namespace MultipleImplementation  
{  
    public interface IShoppingCartRepository  
    {  
        object GetCart();  
    }  
}


enum to select which implementation will be used...

namespace MultipleImplementation  
{  
    public class Constants  
    {  
    }  
  
    public enum CartSource  
    {  
        Cache=1,  
        DB=2,  
        API=3  
    }  
}  


The implementation of the declared repository interface (who will select which implementation... )

using System;  
  
namespace MultipleImplementation  
{  
    public class ShoppingCartRepository : IShoppingCartRepository  
    {  
        private readonly Func<string, IShoppingCart> shoppingCart;  
        public ShoppingCartRepository(Func<string, IShoppingCart> shoppingCart)  
        {  
            this.shoppingCart = shoppingCart;  
        }  
  
        public object GetCart()  
        {  
            return shoppingCart(CartSource.DB.ToString()).GetCart();  
        }  
    }  
}  


Finally, packing all together in the startup.cs file, in ConfigureService method

public void ConfigureServices(IServiceCollection services)  
        {  
  
            services.AddScoped<IShoppingCartRepository, ShoppingCartRepository>();  
  
            services.AddSingleton<ShoppingCartCache>();  
            services.AddSingleton<ShoppingCartDB>();  
            services.AddSingleton<ShoppingCartAPI>();  
  
            services.AddTransient<Func<string, IShoppingCart>>(serviceProvider => key =>  
            {  
                switch (key)  
                {  
                    case "API":  
                        return serviceProvider.GetService<ShoppingCartAPI>();  
                    case "DB":  
                        return serviceProvider.GetService<ShoppingCartDB>();  
                    default:  
                        return serviceProvider.GetService<ShoppingCartCache>();  
                }  
            });  
  
            services.AddMvc();  
        }  

There, I reinforce, that a 6 min read will clear the mind to help you solve multiple implementations into one interface. Good luck!

Beerbohm answered 7/4, 2021 at 21:34 Comment(1)
I like this one :)Bedelia
K
8

My solution for what it's worth... considered switching to Castle Windsor as can't say I liked any of the solutions above. Sorry!!

public interface IStage<out T> : IStage { }

public interface IStage {
      void DoSomething();
}

Create your various implementations

public class YourClassA : IStage<YouClassA> { 
    public void DoSomething() 
    {
        ...TODO
    }
}

public class YourClassB : IStage<YourClassB> { .....etc. }

Registration

services.AddTransient<IStage<YourClassA>, YourClassA>()
services.AddTransient<IStage<YourClassB>, YourClassB>()

Constructor and instance usage...

public class Whatever
{
   private IStage ClassA { get; }

   public Whatever(IStage<YourClassA> yourClassA)
   {
         ClassA = yourClassA;
   }

   public void SomeWhateverMethod()
   {
        ClassA.DoSomething();
        .....
   }
Kazue answered 14/8, 2018 at 15:44 Comment(2)
What would you do if your Whatever class, OP asked for controller, so I will assume this is the controller, needs 15 different services? Do you want to add them to the constructor?Sharpen
I like this one :)Bedelia
C
6

I think the solution described in the following article "Resolución dinámica de tipos en tiempo de ejecución en el contenedor de IoC de .NET Core" is simpler and does not require factories.

You could use a generic interface

public interface IService<T> where T : class {}

then register the desired types on the IoC container:

services.AddTransient<IService<ServiceA>, ServiceA>();
services.AddTransient<IService<ServiceB>, ServiceB>();

After that you must declare the dependencies as follow:

private readonly IService<ServiceA> _serviceA;
private readonly IService<ServiceB> _serviceB;

public WindowManager(IService<ServiceA> serviceA, IService<ServiceB> serviceB)
{
    this._serviceA = serviceA ?? throw new ArgumentNullException(nameof(serviceA));
    this._serviceB = serviceB ?? throw new ArgumentNullException(nameof(ServiceB));
}
Crespo answered 28/1, 2021 at 15:32 Comment(1)
This is the perfect solutionCircumference
Q
6

Modular extension class solution

Very late answer, but this is the way I do it, which has some advantages over some of the other solutions to this question.

Advantages:

  • only 1 line of code per service implementation registration, no extra logic necessary in the registration method
  • the keyed services do not need to all be registered at the same time and/or place. the registrations can even be done in different projects if that is what is needed, as long as the keys are unique. this allows new implementations to be added completely modularly.
  • service instantiation is lazy (+ thread safe), so no unnecessary activation of all implementations when only one or a few are used.
  • no dependency on any external delegate or type in your code, the service is injected as a plain Func<TKey, TService> by default, but it is easy to register a custom delegate or type if you prefer
  • easy to choose between Transient, Singleton, or Scoped registration for the factory
  • use any key type you like (I do strongly suggest you just use simple types with build-in efficient equality comparison like an int, string, enum, or bool because why make life more complicated than it needs to be)

Configuration examples:

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    // default instantiation:
    services.AddKeyedService<IService, ImplementationA, string>("A", ServiceLifetime.Scoped);

    // using an implementation factory to pass a connection string to the constructor:
    services.AddKeyedService<IService, ImplementationB, string>("B", x => {
        var connectionString = ConfigurationManager.ConnectionStrings["mongo"].ConnectionString;
        return new ImplementationB(connectionString);
    }, ServiceLifetime.Scoped);

    // using a custom delegate instead of Func<TKey, TService>
    services.AddKeyedService<IService, ImplementationC, string, StringKeyedService>(
        "C", (_, x) => new StringKeyedService(x), ServiceLifetime.Singleton);

    return services.BuildServiceProvider();
}

public delegate IService StringKeyedService(string key);

Usage examples:

public ExampleClass(Func<string, IService> keyedServiceFactory, StringKeyedService<IService> keyedServiceDelegate)
{
    var serviceKey = Configuration.GetValue<string>("IService.Key");
    var service = keyedServiceFactory(serviceKey);
    var serviceC = keyedServiceDelegate("C");
}

Implementation:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Microsoft.Extensions.DependencyInjection;

public static class KeyedServiceExtensions
{
    // Use this to register TImplementation as TService, injectable as Func<TKey, TService>.
    // Uses default instance activator.
    public static IServiceCollection AddKeyedService<TService, TImplementation, TKey>(this IServiceCollection services, TKey key, ServiceLifetime serviceLifetime)
        where TService : class
        where TImplementation : class, TService
    {
        services.AddTransient<TImplementation>();

        var keyedServiceBuilder = services.CreateOrUpdateKeyedServiceBuilder<TKey, TService, Func<TKey, TService>>(
            DefaultImplementationFactory<TKey, TService>, serviceLifetime);
        keyedServiceBuilder.Add<TImplementation>(key);

        return services;
    }

    // Use this to register TImplementation as TService, injectable as Func<TKey, TService>.
    // Uses implementationFactory to create instances
    public static IServiceCollection AddKeyedService<TService, TImplementation, TKey>(this IServiceCollection services, TKey key,
        Func<IServiceProvider, TImplementation> implementationFactory, ServiceLifetime serviceLifetime)
        where TService : class
        where TImplementation : class, TService
    {
        services.AddTransient(implementationFactory);

        var keyedServiceBuilder = services.CreateOrUpdateKeyedServiceBuilder<TKey, TService, Func<TKey, TService>>(
            DefaultImplementationFactory<TKey, TService>, serviceLifetime);
        keyedServiceBuilder.Add<TImplementation>(key);

        return services;
    }

    // Use this to register TImplementation as TService, injectable as TInjection.
    // Uses default instance activator.
    public static IServiceCollection AddKeyedService<TService, TImplementation, TKey, TInjection>(this IServiceCollection services, TKey key,
        Func<IServiceProvider, Func<TKey, TService>, TInjection> serviceFactory, ServiceLifetime serviceLifetime)
        where TService : class
        where TImplementation : class, TService
        where TInjection : class
    {
        services.AddTransient<TImplementation>();

        var keyedServiceBuilder = services.CreateOrUpdateKeyedServiceBuilder<TKey, TService, TInjection>(
            x => serviceFactory(x, DefaultImplementationFactory<TKey, TService>(x)), serviceLifetime);
        keyedServiceBuilder.Add<TImplementation>(key);

        return services;
    }

    // Use this to register TImplementation as TService, injectable as TInjection.
    // Uses implementationFactory to create instances
    public static IServiceCollection AddKeyedService<TService, TImplementation, TKey, TInjection>(this IServiceCollection services, TKey key,
        Func<IServiceProvider, TImplementation> implementationFactory, Func<IServiceProvider, Func<TKey, TService>, TInjection> serviceFactory, ServiceLifetime serviceLifetime)
        where TService : class
        where TImplementation : class, TService
        where TInjection : class
    {
        services.AddTransient(implementationFactory);

        var keyedServiceBuilder = services.CreateOrUpdateKeyedServiceBuilder<TKey, TService, TInjection>(
            x => serviceFactory(x, DefaultImplementationFactory<TKey, TService>(x)), serviceLifetime);
        keyedServiceBuilder.Add<TImplementation>(key);

        return services;
    }

    private static KeyedServiceBuilder<TKey, TService> CreateOrUpdateKeyedServiceBuilder<TKey, TService, TInjection>(this IServiceCollection services,
        Func<IServiceProvider, TInjection> serviceFactory, ServiceLifetime serviceLifetime)
        where TService : class
        where TInjection : class
    {
        var builderServiceDescription = services.SingleOrDefault(x => x.ServiceType == typeof(KeyedServiceBuilder<TKey, TService>));
        KeyedServiceBuilder<TKey, TService> keyedServiceBuilder;
        if (builderServiceDescription is null)
        {
            keyedServiceBuilder = new KeyedServiceBuilder<TKey, TService>();
            services.AddSingleton(keyedServiceBuilder);

            switch (serviceLifetime)
            {
                case ServiceLifetime.Singleton:
                    services.AddSingleton(serviceFactory);
                    break;
                case ServiceLifetime.Scoped:
                    services.AddScoped(serviceFactory);
                    break;
                case ServiceLifetime.Transient:
                    services.AddTransient(serviceFactory);
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(serviceLifetime), serviceLifetime, "Invalid value for " + nameof(serviceLifetime));
            }
        }
        else
        {
            CheckLifetime<KeyedServiceBuilder<TKey, TService>>(builderServiceDescription.Lifetime, ServiceLifetime.Singleton);

            var factoryServiceDescriptor = services.SingleOrDefault(x => x.ServiceType == typeof(TInjection));
            CheckLifetime<TInjection>(factoryServiceDescriptor.Lifetime, serviceLifetime);

            keyedServiceBuilder = (KeyedServiceBuilder<TKey, TService>)builderServiceDescription.ImplementationInstance;
        }

        return keyedServiceBuilder;

        static void CheckLifetime<T>(ServiceLifetime actual, ServiceLifetime expected)
        {
            if (actual != expected)
                throw new ApplicationException($"{typeof(T).FullName} is already registered with a different ServiceLifetime. Expected: '{expected}', Actual: '{actual}'");
        }
    }

    private static Func<TKey, TService> DefaultImplementationFactory<TKey, TService>(IServiceProvider x) where TService : class
        => x.GetRequiredService<KeyedServiceBuilder<TKey, TService>>().Build(x);

    private sealed class KeyedServiceBuilder<TKey, TService>
    {
        private readonly Dictionary<TKey, Type> _serviceImplementationTypes = new Dictionary<TKey, Type>();

        internal void Add<TImplementation>(TKey key) where TImplementation : class, TService
        {
            if (_serviceImplementationTypes.TryGetValue(key, out var type) && type == typeof(TImplementation))
                return; //this type is already registered under this key

            _serviceImplementationTypes[key] = typeof(TImplementation);
        }

        internal Func<TKey, TService> Build(IServiceProvider serviceProvider)
        {
            var serviceTypeDictionary = _serviceImplementationTypes.Values.Distinct()
                .ToDictionary(
                    type => type,
                    type => new Lazy<TService>(
                        () => (TService)serviceProvider.GetRequiredService(type),
                        LazyThreadSafetyMode.ExecutionAndPublication
                    )
                );
            var serviceDictionary = _serviceImplementationTypes
                .ToDictionary(kvp => kvp.Key, kvp => serviceTypeDictionary[kvp.Value]);

            return key => serviceDictionary[key].Value;
        }
    }
}

it's also possible to make a fluid interface on top of this, let me know if there is interest in that.

Example fluid usage:

var keyedService = services.KeyedSingleton<IService, ServiceKey>()
    .As<ICustomKeyedService<TKey, IService>>((_, x) => new CustomKeyedServiceInterface<ServiceKey, IService>(x));
keyedService.Key(ServiceKey.A).Add<ServiceA>();
keyedService.Key(ServiceKey.B).Add(x => {
    x.GetService<ILogger>.LogDebug("Instantiating ServiceB");
    return new ServiceB();
});
Qualitative answered 18/4, 2021 at 23:22 Comment(1)
KeyedSingleton will be in .net 8 available for Microsoft.Extensions.DependencyInjectionCloudburst
H
6

I had the same problem and I solved using <T>

My interface:

public interface IProvider<T>
{
    Task<string> GetTotalSearchResults(string searchValue);
}

My services configuration:

var host = Host.CreateDefaultBuilder()
                .ConfigureServices((_, services) =>
                {
                    services.AddSingleton(googleSettings);
                    services.AddSingleton(bingSettings);
                    services.AddSingleton<IProvider<BingProvider>, BingProvider>();
                    services.AddSingleton<IProvider<GoogleProvider>, GoogleProvider>();
                    services.AddSingleton<ISearchManager, SearchManager>();
                });

And the you can use it in your class:

public class SearchManager : ISearchManager
    {
        private readonly IProvider<BingProvider> _bing;
        private readonly IProvider<GoogleProvider> _google;

        public SearchManager(IProvider<BingProvider> bing, IProvider<GoogleProvider> google)
        {
            _bing = bing;
            _google = google;
        }
Hainan answered 5/9, 2021 at 17:15 Comment(1)
A downside is this requires the concrete type to be specified everywhere you need it, instead of in 1 place.Juliannajulianne
D
4

I created my own extension over IServiceCollection used WithName extension:

public static IServiceCollection AddScopedWithName<TService, TImplementation>(this IServiceCollection services, string serviceName)
        where TService : class
        where TImplementation : class, TService
    {
        Type serviceType = typeof(TService);
        Type implementationServiceType = typeof(TImplementation);
        ServiceCollectionTypeMapper.Instance.AddDefinition(serviceType.Name, serviceName, implementationServiceType.AssemblyQualifiedName);
        services.AddScoped<TImplementation>();
        return services;
    }

ServiceCollectionTypeMapper is a singleton instance that maps IService > NameOfService > Implementation where an interface could have many implementations with different names, this allows to register types than we can resolve when wee need and is a different approach than resolve multiple services to select what we want.

 /// <summary>
/// Allows to set the service register mapping.
/// </summary>
public class ServiceCollectionTypeMapper
{
    private ServiceCollectionTypeMapper()
    {
        this.ServiceRegister = new Dictionary<string, Dictionary<string, string>>();
    }

    /// <summary>
    /// Gets the instance of mapper.
    /// </summary>
    public static ServiceCollectionTypeMapper Instance { get; } = new ServiceCollectionTypeMapper();

    private Dictionary<string, Dictionary<string, string>> ServiceRegister { get; set; }

    /// <summary>
    /// Adds new service definition.
    /// </summary>
    /// <param name="typeName">The name of the TService.</param>
    /// <param name="serviceName">The TImplementation name.</param>
    /// <param name="namespaceFullName">The TImplementation AssemblyQualifiedName.</param>
    public void AddDefinition(string typeName, string serviceName, string namespaceFullName)
    {
        if (this.ServiceRegister.TryGetValue(typeName, out Dictionary<string, string> services))
        {
            if (services.TryGetValue(serviceName, out _))
            {
                throw new InvalidOperationException($"Exists an implementation with the same name [{serviceName}] to the type [{typeName}].");
            }
            else
            {
                services.Add(serviceName, namespaceFullName);
            }
        }
        else
        {
            Dictionary<string, string> serviceCollection = new Dictionary<string, string>
            {
                { serviceName, namespaceFullName },
            };
            this.ServiceRegister.Add(typeName, serviceCollection);
        }
    }

    /// <summary>
    /// Get AssemblyQualifiedName of implementation.
    /// </summary>
    /// <typeparam name="TService">The type of the service implementation.</typeparam>
    /// <param name="serviceName">The name of the service.</param>
    /// <returns>The AssemblyQualifiedName of the inplementation service.</returns>
    public string GetService<TService>(string serviceName)
    {
        Type serviceType = typeof(TService);

        if (this.ServiceRegister.TryGetValue(serviceType.Name, out Dictionary<string, string> services))
        {
            if (services.TryGetValue(serviceName, out string serviceImplementation))
            {
                return serviceImplementation;
            }
            else
            {
                return null;
            }
        }
        else
        {
            return null;
        }
    }

To register a new service:

services.AddScopedWithName<IService, MyService>("Name");

To resolve service we need an extension over IServiceProvider like this.

/// <summary>
    /// Gets the implementation of service by name.
    /// </summary>
    /// <typeparam name="T">The type of service.</typeparam>
    /// <param name="serviceProvider">The service provider.</param>
    /// <param name="serviceName">The service name.</param>
    /// <returns>The implementation of service.</returns>
    public static T GetService<T>(this IServiceProvider serviceProvider, string serviceName)
    {
        string fullnameImplementation = ServiceCollectionTypeMapper.Instance.GetService<T>(serviceName);
        if (fullnameImplementation == null)
        {
            throw new InvalidOperationException($"Unable to resolve service of type [{typeof(T)}] with name [{serviceName}]");
        }
        else
        {
            return (T)serviceProvider.GetService(Type.GetType(fullnameImplementation));
        }
    }

When resolve:

serviceProvider.GetService<IWithdrawalHandler>(serviceName);

Remember that serviceProvider can be injected within a constructor in our application as IServiceProvider.

I hope this helps.

Drudgery answered 27/2, 2020 at 16:15 Comment(0)
D
3

While it seems @Miguel A. Arilla has pointed it out clearly and I voted up for him, I created on top of his useful solution another solution which looks neat but requires a lot more work.

It definitely depends on the above solution. So basically I created something similar to Func<string, IService>> and I called it IServiceAccessor as an interface and then I had to add a some more extensions to the IServiceCollection as such:

public static IServiceCollection AddSingleton<TService, TImplementation, TServiceAccessor>(
            this IServiceCollection services,
            string instanceName
        )
            where TService : class
            where TImplementation : class, TService
            where TServiceAccessor : class, IServiceAccessor<TService>
        {
            services.AddSingleton<TService, TImplementation>();
            services.AddSingleton<TServiceAccessor>();
            var provider = services.BuildServiceProvider();
            var implementationInstance = provider.GetServices<TService>().Last();
            var accessor = provider.GetServices<TServiceAccessor>().First();

            var serviceDescriptors = services.Where(d => d.ServiceType == typeof(TServiceAccessor));
            while (serviceDescriptors.Any())
            {
                services.Remove(serviceDescriptors.First());
            }

            accessor.SetService(implementationInstance, instanceName);
            services.AddSingleton<TServiceAccessor>(prvd => accessor);
            return services;
        }

The service Accessor looks like:

 public interface IServiceAccessor<TService>
    {
         void Register(TService service,string name);
         TService Resolve(string name);

    }

The end result,you will be able to register services with names or named instances like we used to do with other containers..for instance:

    services.AddSingleton<IEncryptionService, SymmetricEncryptionService, EncyptionServiceAccessor>("Symmetric");
    services.AddSingleton<IEncryptionService, AsymmetricEncryptionService, EncyptionServiceAccessor>("Asymmetric");

That is enough for now, but to make your work complete, it is better to add more extension methods as you can to cover all types of registrations following the same approach.

There was another post on stackoverflow, but I can not find it, where the poster has explained in details why this feature is not supported and how to work around it, basically similar to what @Miguel stated. It was nice post even though I do not agree with each point because I think there are situation where you really need named instances. I will post that link here once I find it again.

As a matter of fact, you do not need to pass that Selector or Accessor:

I am using the following code in my project and it worked well so far.

 /// <summary>
    /// Adds the singleton.
    /// </summary>
    /// <typeparam name="TService">The type of the t service.</typeparam>
    /// <typeparam name="TImplementation">The type of the t implementation.</typeparam>
    /// <param name="services">The services.</param>
    /// <param name="instanceName">Name of the instance.</param>
    /// <returns>IServiceCollection.</returns>
    public static IServiceCollection AddSingleton<TService, TImplementation>(
        this IServiceCollection services,
        string instanceName
    )
        where TService : class
        where TImplementation : class, TService
    {
        var provider = services.BuildServiceProvider();
        var implementationInstance = provider.GetServices<TService>().LastOrDefault();
        if (implementationInstance.IsNull())
        {
            services.AddSingleton<TService, TImplementation>();
            provider = services.BuildServiceProvider();
            implementationInstance = provider.GetServices<TService>().Single();
        }
        return services.RegisterInternal(instanceName, provider, implementationInstance);
    }

    private static IServiceCollection RegisterInternal<TService>(this IServiceCollection services,
        string instanceName, ServiceProvider provider, TService implementationInstance)
        where TService : class
    {
        var accessor = provider.GetServices<IServiceAccessor<TService>>().LastOrDefault();
        if (accessor.IsNull())
        {
            services.AddSingleton<ServiceAccessor<TService>>();
            provider = services.BuildServiceProvider();
            accessor = provider.GetServices<ServiceAccessor<TService>>().Single();
        }
        else
        {
            var serviceDescriptors = services.Where(d => d.ServiceType == typeof(IServiceAccessor<TService>));
            while (serviceDescriptors.Any())
            {
                services.Remove(serviceDescriptors.First());
            }
        }
        accessor.Register(implementationInstance, instanceName);
        services.AddSingleton<TService>(prvd => implementationInstance);
        services.AddSingleton<IServiceAccessor<TService>>(prvd => accessor);
        return services;
    }

    //
    // Summary:
    //     Adds a singleton service of the type specified in TService with an instance specified
    //     in implementationInstance to the specified Microsoft.Extensions.DependencyInjection.IServiceCollection.
    //
    // Parameters:
    //   services:
    //     The Microsoft.Extensions.DependencyInjection.IServiceCollection to add the service
    //     to.
    //   implementationInstance:
    //     The instance of the service.
    //   instanceName:
    //     The name of the instance.
    //
    // Returns:
    //     A reference to this instance after the operation has completed.
    public static IServiceCollection AddSingleton<TService>(
        this IServiceCollection services,
        TService implementationInstance,
        string instanceName) where TService : class
    {
        var provider = services.BuildServiceProvider();
        return RegisterInternal(services, instanceName, provider, implementationInstance);
    }

    /// <summary>
    /// Registers an interface for a class
    /// </summary>
    /// <typeparam name="TInterface">The type of the t interface.</typeparam>
    /// <param name="services">The services.</param>
    /// <returns>IServiceCollection.</returns>
    public static IServiceCollection As<TInterface>(this IServiceCollection services)
         where TInterface : class
    {
        var descriptor = services.Where(d => d.ServiceType.GetInterface(typeof(TInterface).Name) != null).FirstOrDefault();
        if (descriptor.IsNotNull())
        {
            var provider = services.BuildServiceProvider();
            var implementationInstance = (TInterface)provider?.GetServices(descriptor?.ServiceType)?.Last();
            services?.AddSingleton(implementationInstance);
        }
        return services;
    }
Davit answered 1/6, 2018 at 10:3 Comment(1)
This helped solve my problem where I was losing registration of types in the service accessor. The trick was to remove all bindings for the service accessor and then add it again!Endothelium
M
3

I have created a library for this that implements some nice features. Code can be found on GitHub: https://github.com/dazinator/Dazinator.Extensions.DependencyInjection NuGet: https://www.nuget.org/packages/Dazinator.Extensions.DependencyInjection/

Usage is straightforward:

  1. Add the Dazinator.Extensions.DependencyInjection nuget package to your project.
  2. Add your Named Service registrations.
    var services = new ServiceCollection();
    services.AddNamed<AnimalService>(names =>
    {
        names.AddSingleton("A"); // will resolve to a singleton instance of AnimalService
        names.AddSingleton<BearService>("B"); // will resolve to a singleton instance of BearService (which derives from AnimalService)
        names.AddSingleton("C", new BearService()); will resolve to singleton instance provided yourself.
        names.AddSingleton("D", new DisposableTigerService(), registrationOwnsInstance = true); // will resolve to singleton instance provided yourself, but will be disposed for you (if it implements IDisposable) when this registry is disposed (also a singleton).

        names.AddTransient("E"); // new AnimalService() every time..
        names.AddTransient<LionService>("F"); // new LionService() every time..

        names.AddScoped("G");  // scoped AnimalService
        names.AddScoped<DisposableTigerService>("H");  scoped DisposableTigerService and as it implements IDisposable, will be disposed of when scope is disposed of.

    });


In the example above, notice that for each named registration, you are also specifying the lifetime or Singleton, Scoped, or Transient.

You can resolve services in one of two ways, depending on if you are comfortable with having your services take a dependency on this package of not:

public MyController(Func<string, AnimalService> namedServices)
{
   AnimalService serviceA = namedServices("A");
   AnimalService serviceB = namedServices("B"); // BearService derives from AnimalService
}

or

public MyController(NamedServiceResolver<AnimalService> namedServices)
{
   AnimalService serviceA = namedServices["A"];
   AnimalService serviceB = namedServices["B"]; // instance of BearService returned derives from AnimalService
}

I have specifically designed this library to work well with Microsoft.Extensions.DependencyInjection - for example:

  1. When you register named services, any types that you register can have constructors with parameters - they will be satisfied via DI, in the same way that AddTransient<>, AddScoped<> and AddSingleton<> methods work ordinarily.

  2. For transient and scoped named services, the registry builds an ObjectFactory so that it can activate new instances of the type very quickly when needed. This is much faster than other approaches and is in line with how Microsoft.Extensions.DependencyInjection does things.

Meredith answered 12/3, 2020 at 0:29 Comment(0)
C
3

I know this post is a couple years old, but I keep running into this and I'm not happy with the service locator pattern.

Also, I know the OP is looking for an implementation which allows you to choose a concrete implementation based on a string. I also realize that the OP is specifically asking for an implementation of an identical interface. The solution I'm about to describe relies on adding a generic type parameter to your interface. The problem is that you don't have any real use for the type parameter other than service collection binding. I'll try to describe a situation which might require something like this.

Imagine configuration for such a scenario in appsettings.json which might look something like this (this is just for demonstration, your configuration can come from wherever you want as long as you have the correction configuration provider):

{
  "sqlDataSource": {
    "connectionString": "Data Source=localhost; Initial catalog=Foo; Connection Timeout=5; Encrypt=True;",
    "username": "foo",
    "password": "this normally comes from a secure source, but putting here for demonstration purposes"
  },
  "mongoDataSource": {
    "hostName": "uw1-mngo01-cl08.company.net",
    "port": 27026,
    "collection": "foo"
  }
}

You really need a type that represents each of your configuration options:

public class SqlDataSource
{
  public string ConnectionString { get;set; }
  public string Username { get;set; }
  public string Password { get;set; }
}

public class MongoDataSource
{
  public string HostName { get;set; }
  public string Port { get;set; }
  public string Collection { get;set; }
}

Now, I know that it might seem a little contrived to have two implementations of the same interface, but it I've definitely seen it in more than one case. The ones I usually come across are:

  1. When migrating from one data store to another, it's useful to be able to implement the same logical operations using the same interfaces so that you don't need to change the calling code. This also allows you to add configuration which swaps between different implementations at runtime (which can be useful for rollback).
  2. When using the decorator pattern. The reason you might use that pattern is that you want to add functionality without changing the interface and fall back to the existing functionality in certain cases (I've used it when adding caching to repository classes because I want circuit breaker-like logic around connections to the cache that fall back to the base repository -- this gives me optimal behavior when the cache is available, but behavior that still functions when it's not).

Anyway, you can reference them by adding a type parameter to your service interface so that you can implement the different implementations:

public interface IService<T> {
  void DoServiceOperation();
}

public class MongoService : IService<MongoDataSource> {
  private readonly MongoDataSource _options;

  public FooService(IOptionsMonitor<MongoDataSource> serviceOptions){
    _options = serviceOptions.CurrentValue
  }

  void DoServiceOperation(){
    //do something with your mongo data source options (connect to database)
    throw new NotImplementedException();
  }
}

public class SqlService : IService<SqlDataSource> {
  private readonly SqlDataSource_options;

  public SqlService (IOptionsMonitor<SqlDataSource> serviceOptions){
    _options = serviceOptions.CurrentValue
  }

  void DoServiceOperation(){
    //do something with your sql data source options (connect to database)
    throw new NotImplementedException();
  }
}

In startup, you'd register these with the following code:

services.Configure<SqlDataSource>(configurationSection.GetSection("sqlDataSource"));
services.Configure<MongoDataSource>(configurationSection.GetSection("mongoDataSource"));

services.AddTransient<IService<SqlDataSource>, SqlService>();
services.AddTransient<IService<MongoDataSource>, MongoService>();

Finally in the class which relies on the Service with a different connection, you just take a dependency on the service you need and the DI framework will take care of the rest:

[Route("api/v1)]
[ApiController]
public class ControllerWhichNeedsMongoService {  
  private readonly IService<MongoDataSource> _mongoService;
  private readonly IService<SqlDataSource> _sqlService ;

  public class ControllerWhichNeedsMongoService(
    IService<MongoDataSource> mongoService, 
    IService<SqlDataSource> sqlService
  )
  {
    _mongoService = mongoService;
    _sqlService = sqlService;
  }

  [HttpGet]
  [Route("demo")]
  public async Task GetStuff()
  {
    if(useMongo)
    {
       await _mongoService.DoServiceOperation();
    }
    await _sqlService.DoServiceOperation();
  }
}

These implementations can even take a dependency on each other. The other big benefit is that you get compile-time binding so any refactoring tools will work correctly.

Hope this helps someone in the future.

Calypso answered 14/5, 2020 at 1:56 Comment(1)
the problem arises when you have more than one mongo connection configs. For me i will end up repeating this line 3 times with diff config names. services.Configure<MongoDataSource>(configurationSection.GetSection("mongoDataSource")); The solution seems to create multiple same models with diff names `for example StockMongoDataSource, ETFMongoDataSource etcPlantain
C
2

Any technical way using IEnumerable<Interface> effectively defeats the whole purpose of DI since you need to select which implementation you need to resolve to and might be pointing to bad design.

The workaround for this issue that worked for me was to separate usage and create separate interfaces like so

public interface ICRUDService<T> where T : class
{
    void CreateAndSetId(T item);
    void Delete(int id);
    ActionResult<List<T>> GetAll();
    ActionResult<T> GetById(int id);
    void Update(int id, T item);
}

Then the individual interfaces

public interface ITodoService : ICRUDService<Todo> {}
public interface IValuesService : ICRUDService<Values> {}

And their implementations

public class TodoService : ITodoService { ... }
public class ValuesService : IValuesService { ... }

Startup.ConfigureServices

services.AddScoped<ITodoService, TodoService>();
services.AddScoped<IValuesService, ValuesService>();

Usage

public class UsageClass {
 public UsageClass(ITodoService todoService, IValuesService valuesService) {}
}

If you are still interested in resolving multiple implementations, THIS is the Microsoft recommendation. Just linking it here since this isn't what I recommend.

Carpetbag answered 10/3, 2021 at 0:50 Comment(0)
O
2

In .Net 8 we can use AddKeyed...() method to register our services to IoC. To use our services we can use FromKeyedServices() attribute.

Registring services to IoC:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddKeyedSingleton<INotificationService, 
SmsNotificationService>("sms");
builder.Services.AddKeyedSingleton<INotificationService, 
EmailNotificationService>("email");
builder.Services.AddKeyedSingleton<INotificationService, 
PushNotificationService>("push");

Using DI:

public class SmsWrapper([FromKeyedServices("sms")] INotificationService sms)
{
    public string Notify(string message) => sms.Notify(message);
}


public class EmailWrapper([FromKeyedServices("email")] INotificationService email)
{
    public string Notify(string message) => email.Notify(message);
}

for more information: https://andrewlock.net/exploring-the-dotnet-8-preview-keyed-services-dependency-injection-support/

Olivette answered 29/3 at 9:39 Comment(0)
S
1

Extending the solution of @rnrneverdies. Instead of ToString(), following options can also be used- 1) With common property implementation, 2) A service of services suggested by @Craig Brunetti.

public interface IService { }
public class ServiceA : IService
{
    public override string ToString()
    {
        return "A";
    }
}

public class ServiceB : IService
{
    public override string ToString()
    {
        return "B";
    }

}

/// <summary>
/// extension method that compares with ToString value of an object and returns an object if found
/// </summary>
public static class ServiceProviderServiceExtensions
{
    public static T GetService<T>(this IServiceProvider provider, string identifier)
    {
        var services = provider.GetServices<T>();
        var service = services.FirstOrDefault(o => o.ToString() == identifier);
        return service;
    }
}

public void ConfigureServices(IServiceCollection services)
{
    //Initials configurations....

    services.AddSingleton<IService, ServiceA>();
    services.AddSingleton<IService, ServiceB>();
    services.AddSingleton<IService, ServiceC>();

    var sp = services.BuildServiceProvider();
    var a = sp.GetService<IService>("A"); //returns instance of ServiceA
    var b = sp.GetService<IService>("B"); //returns instance of ServiceB

    //Remaining configurations....
}
Sherbrooke answered 11/6, 2019 at 4:33 Comment(0)
E
1

After reading the answers here and articles elsewhere I was able to get it working without strings. When you have multiple implementations of the same interface the DI will add these to a collection, so it's then possible to retrieve the version you want from the collection using typeof.

// In Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped(IService, ServiceA);
    services.AddScoped(IService, ServiceB);
    services.AddScoped(IService, ServiceC);
}

// Any class that uses the service(s)
public class Consumer
{
    private readonly IEnumerable<IService> _myServices;

    public Consumer(IEnumerable<IService> myServices)
    {
        _myServices = myServices;
    }

    public UseServiceA()
    {
        var serviceA = _myServices.FirstOrDefault(t => t.GetType() == typeof(ServiceA));
        serviceA.DoTheThing();
    }

    public UseServiceB()
    {
        var serviceB = _myServices.FirstOrDefault(t => t.GetType() == typeof(ServiceB));
        serviceB.DoTheThing();
    }

    public UseServiceC()
    {
        var serviceC = _myServices.FirstOrDefault(t => t.GetType() == typeof(ServiceC));
        serviceC.DoTheThing();
    }
}
Eger answered 27/11, 2019 at 9:50 Comment(4)
Defeats the purpose of IoC. You might as well just write: var serviceA = new ServiceA();Mortarboard
@JamesCurran not if ServiceA has dependencies, or if you want to unit test the class.Sorely
This is useful when you're dealing with singletons or you want to get a scoped instance.Swelling
What if you have 150+ Services?Sharpen
E
1

Okay, here a clean and readable answer by using a dictionary

Create a enum with your database Key Name

public enum Database
    {
        Red,
        Blue
    }

In Startup.cs, create a dictionary of function that open a new SqlConnection, then inject the dependency dictionary as Singleton

Dictionary<Database, Func<IDbConnection>> connectionFactory = new()
   {
      { Database.Red, () => new SqlConnection(Configuration.GetConnectionString("RedDatabase")) },
      { Database.Blue, () => new SqlConnection(Configuration.GetConnectionString("BlueDatabase")) }
   };
services.AddSingleton(connectionFactory);

After you can get the instance od the dependency on object constructor like so:

public class ObjectQueries
{
   private readonly IDbConnection _redConnection;
   private readonly IDbConnection _blueConnection;

   public ObjectQueries(Dictionary<Database, Func<IDbConnection>> connectionFactory)
   {
      _redConnection = connectionFactory[Database.Red]();
      _blueConnection = connectionFactory[Database.Blue]();
   }
}

Thanks @Stefan Steiger for the Idea ;)

Ernestineernesto answered 5/3, 2021 at 9:51 Comment(0)
M
1

I really am super late to the party, but this is so super easy to solve without any kind of factory pattern or complicated jiggery...

I faced this problem and came up with a super simple approach. All you need is a container to put your object in and then register the container.

So assuming you have this (which is completely reusable):

public class DependencyRegistration<TScope, TDependency>
{
    public TDependency Dependency { get; }

    public DependencyRegistration(TDependency dependency)
    {
        Dependency = dependency;
    }
}

Then you can register your services 'dependently':

.AddSingleton(serviceProvider =>
{
    return new DependencyRegistration<IWidgetRepository, string>("the connection string");
})
.AddSingleton(serviceProvider =>
{
    return new DependencyRegistration<IRestRequest, string>("the URL");
})

And because the TScope effectively takes the place of the named registration you just consume them thus:

[Inject]
public DependencyRegistration<IWidgetRepository, string> widgetConnectionStringRegistration { get; set; }

private string widgetConnectionString => widgetConnectionStringRegistration.Dependency;

So no violations of principles at all, and all you need is a unique type to use for the scope - use anything that makes sense or roll your own definition if you want to be semantically precise:

public class TokenContext
{
    public interface IAdministrationToken { }
    public interface IUserToken { }
}

And so:

.AddSingleton(serviceProvider =>
{
    return new DependencyRegistration<TokenContext.IUserToken, string>("the user token");
})
.AddSingleton(serviceProvider =>
{
    return new DependencyRegistration<TokenContext.IAdministrationToken, string>("the admin token");
})

And instead of the 'GetRequiredService' extension method, I created a 'GetRequiredRegisteredService' version to more easily resolve interdependent dependencies:

.AddSingleton(serviceProvider =>
{
    var myURL = serviceProvider.GetRequiredRegisteredService<IRestRequest, string>();
    return new new RestRequest(myURL);
})

Once you see how deceptively easy the fix is, it's then super easy to use.

Oh, and even super easier to install:

Install-Package CodeRed.Extensions.DependencyInjection.DependencyRegister

Enjoy!

Paul

Melba answered 11/8, 2021 at 3:59 Comment(0)
B
0

While the out of the box implementation doesn't offer it, here's a sample project that allows you to register named instances, and then inject INamedServiceFactory into your code and pull out instances by name. Unlike other facory solutions here, it will allow you to register multiple instances of same implementation but configured differently

https://github.com/macsux/DotNetDINamedInstances

Bottle answered 3/5, 2018 at 17:50 Comment(0)
S
0

How about a service for services?

If we had an INamedService interface (with .Name property), we could write an IServiceCollection extension for .GetService(string name), where the extension would take that string parameter, and do a .GetServices() on itself, and in each returned instance, find the instance whose INamedService.Name matches the given name.

Like this:

public interface INamedService
{
    string Name { get; }
}

public static T GetService<T>(this IServiceProvider provider, string serviceName)
    where T : INamedService
{
    var candidates = provider.GetServices<T>();
    return candidates.FirstOrDefault(s => s.Name == serviceName);
}

Therefore, your IMyService must implement INamedService, but you'll get the key-based resolution you want, right?

To be fair, having to even have this INamedService interface seems ugly, but if you wanted to go further and make things more elegant, then a [NamedServiceAttribute("A")] on the implementation/class could be found by the code in this extension, and it'd work just as well. To be even more fair, Reflection is slow, so an optimization may be in order, but honestly that's something the DI engine should've been helping with. Speed and simplicity are each grand contributors to TCO.

All in all, there's no need for an explicit factory, because "finding a named service" is such a reusable concept, and factory classes don't scale as a solution. And a Func<> seems fine, but a switch block is so bleh, and again, you'll be writing Funcs as often as you'd be writing Factories. Start simple, reusable, with less code, and if that turns out not to do it for ya, then go complex.

Svetlana answered 28/8, 2018 at 20:51 Comment(4)
This is called the service locator pattern is and is typically not the best route to go unless you absolutely have toDogger
@JoePhillips Do you have some input as to why its not a good solution? i love the elegance of it. The only downside i can think of is that i creates a instance of all of them everytime you get one.Andress
@Andress The main reason is because it is very very hard to work with. If you are passing in a serviceLocator object into a class, it is not obvious at all what dependencies that class uses since it's getting them all from a magic "god" object. Imagine having to find references of the type you want to change. That ability basically disappears when you're getting everything through a service locator object. Constructor injection is far more clear and reliableDogger
I dunno. The obviousness is not a minus for me... because if I cared about keeping track of how my components leverage their dependencies, I'd have unit tests for that... tests that not only refer to each dependency, but help us understand HOW each dependency is needed. How else are you going to be aware of that, by reading constructors?!?Svetlana
P
0

I have run into the same problem and I worked with a simple extension to allow Named services. You can find it here:

It allows you to add as many (named) services as you want like this:

 var serviceCollection = new ServiceCollection();
 serviceCollection.Add(typeof(IMyService), typeof(MyServiceA), "A", ServiceLifetime.Transient);
 serviceCollection.Add(typeof(IMyService), typeof(MyServiceB), "B", ServiceLifetime.Transient);

 var serviceProvider = serviceCollection.BuildServiceProvider();

 var myServiceA = serviceProvider.GetService<IMyService>("A");
 var myServiceB = serviceProvider.GetService<IMyService>("B");

The library also allows you to easy implement a "factory pattern" like this:

    [Test]
    public void FactoryPatternTest()
    {
        var serviceCollection = new ServiceCollection();
        serviceCollection.Add(typeof(IMyService), typeof(MyServiceA), MyEnum.A.GetName(), ServiceLifetime.Transient);
        serviceCollection.Add(typeof(IMyService), typeof(MyServiceB), MyEnum.B.GetName(), ServiceLifetime.Transient);

        serviceCollection.AddTransient<IMyServiceFactoryPatternResolver, MyServiceFactoryPatternResolver>();

        var serviceProvider = serviceCollection.BuildServiceProvider();

        var factoryPatternResolver = serviceProvider.GetService<IMyServiceFactoryPatternResolver>();

        var myServiceA = factoryPatternResolver.Resolve(MyEnum.A);
        Assert.NotNull(myServiceA);
        Assert.IsInstanceOf<MyServiceA>(myServiceA);

        var myServiceB = factoryPatternResolver.Resolve(MyEnum.B);
        Assert.NotNull(myServiceB);
        Assert.IsInstanceOf<MyServiceB>(myServiceB);
    }

    public interface IMyServiceFactoryPatternResolver : IFactoryPatternResolver<IMyService, MyEnum>
    {
    }

    public class MyServiceFactoryPatternResolver : FactoryPatternResolver<IMyService, MyEnum>, IMyServiceFactoryPatternResolver
    {
        public MyServiceFactoryPatternResolver(IServiceProvider serviceProvider)
        : base(serviceProvider)
        {
        }
    }

    public enum MyEnum
    {
        A = 1,
        B = 2
    }

Hope it helps

Phillie answered 29/4, 2020 at 16:51 Comment(0)
S
0

You can use url route instead of enum to resolve service type.

Interface which is typed to a Parent Class where BalanceSheetReportDto : ReportDto

public interface IReportService<T> where T : ReportDto
{
    Task<byte[]> GetFileStream(T reportDto);
}

An abstract class that implements it.

public abstract class ReportService : IReportService<ReportDto>
    {
        public abstract Task<byte[]> GetFileStream(ReportDto reportDto);

    }

This abstract class is what we need to resolve concrete types as you will not be able to specify resolver type as IReportService<ReportDto> and return implementation BalaceSheetReportService. Look at next code block.

Service Resolver for DI.

public delegate ReportService ServiceResolver(ReportType key);
    public static IServiceCollection AddReportServices(this IServiceCollection services)
    {
        services.AddScoped<BalanceSheetReportService>();
        
        services.AddScoped<ServiceResolver>(serviceProvider => key =>  
        {  
            switch (key)  
            {  
                case ReportType.BalanceSheet:  
                    return serviceProvider.GetService<BalanceSheetReportService>();
                default:
                    throw new KeyNotFoundException();  
            }  
        });  

And in controller add resolver but not need to cast to specific type.

public class FinancialReportsController : BaseController
    {
        private ServiceCollectionExtension.ServiceResolver _resolver;
    ...
    [HttpPost("balance-sheet")]              
        public async Task<byte[]> GetBalanceSheetReport([FromBody] BalanceSheetReportDto request)
        {
            try
            {
                var reportService =  _resolver(ReportType.BalanceSheet); //magic
                var data = await reportService.GetFileStream(request);

Concrete implementation.

public class BalanceSheetReportService: ReportService 
    {
    ...
    public override async Task<byte[]> GetFileStream(ReportDto reportDto)
        {
            return await GetFileStream((BalanceSheetReportDto) reportDto);
        }

        private  async Task<byte[]> GetFileStream(BalanceSheetReportDto reportDto)
        {

Unrelated but you could inject other services (data classes for example) to abstract class.

private MongoDbContext _context;
 public ReportService(MongoDbContext context) {
 _context = context;
}

And then in your subclasses call this constructor and be done with it.

public BalanceSheetReportService(MongoDbContext context) : base(context) {}
Sharpen answered 2/2, 2021 at 0:47 Comment(0)
O
0

Most answers either propose a factory method with hard coded instantiation or require all named instances being instantiated in advance. I want a generic solution without instantiating all named services. Here my solution:

Usage:

public MyConsumer(NamedServiceFactory<IMyService> factory) {
  IMyService service = factory.Create("foo");
}

DI registration:

host.ConfigureServices(services => {
  services.AddNamed<IMyService, MyServiceFoo>("foo");
  services.AddNamed<IMyService, MyServiceBar>("bar");
});

Implementation:

namespace Microsoft.Extensions.DependencyInjection
{
    public static class NamedServiceFactoryExtensions
    {
        public static IServiceCollection AddNamed<T>(this IServiceCollection services, string? name, Func<IServiceProvider, T> factory) where T : class
        {
            if (!services.Contains<NamedServiceFactory<T>>())
                services.AddSingleton<NamedServiceFactory<T>>();
            services.AddSingleton<NamedFactory<T>>(sp => new NamedFactory<T>(name, factory));
            return services;
        }

        public static IServiceCollection AddNamed<T, TImpl>(this IServiceCollection services, string? name) where T : class where TImpl : T
            => AddNamed<T>(services, name, sp => sp.GetRequiredService<TImpl>());
    }
}

public class NamedFactory<T>
{
    public string Name { get; private set; }
    private readonly Func<IServiceProvider, T> Factory;

    public NamedFactory(string? name, Func<IServiceProvider, T> factory)
    {
        this.Factory = factory;
        if (string.IsNullOrEmpty(name))
            throw new ArgumentNullException(nameof(name));
        Name = name!;
    }

    public T Create(IServiceProvider serviceProvider) => Factory(serviceProvider);
}

public class NamedServiceFactory<T>
{
    private Dictionary<string, NamedFactory<T>> Factories = new();
    private readonly IServiceProvider ServiceProvider;

    public NamedServiceFactory(IEnumerable<NamedFactory<T>> factories, IServiceProvider serviceProvider)
    {
        this.ServiceProvider = serviceProvider;
        foreach (var factory in factories)
            Factories.Add(factory.Name, factory);
    }

    public T Create(string? name, IServiceProvider? serviceProvider = null)
    {
        if (string.IsNullOrEmpty(name))
            throw new ArgumentNullException(nameof(name));
        if (!Factories.TryGetValue(name!, out var factory))
            throw new InvalidOperationException($"Service not found with name {name}");
        return factory.Create(serviceProvider ?? ServiceProvider);
    }
}

Feel free to add interfaces INamedServiceFactory<T> and INamedFactory<T>.

Orate answered 4/6, 2022 at 9:10 Comment(0)
A
0

In my solution instead of named service registration I use attribute on controller action. In this way I make a choice for using one of the implementations of service in DI scope of the action request.

For example, if I have specified service implementation for reports endpoint, my code looks like this:

 public class HomeController : ControllerBase
  {

    private readonly IService _service;

    public HomeController(IService service)
    {
      _service = service;
    }

    [HttpGet]
    [ReportScope]
    public IEnumerable<WeatherForecast> GetReport()
    {
       //Use Service1 implementation, because of ReportScope attribute
      _service.DoSomeThing();
    }
    
    [HttpGet]
    public IEnumerable<WeatherForecast> Get()
    {
       //Use Service2 implementation
      _service.DoSomeThing();
    }

This attribute is processed by custom middleware:

public class ReportScopeLoggingMiddleware
  {
    private readonly RequestDelegate _next;

    public ReportScopeLoggingMiddleware(RequestDelegate next)
    {
      _next = next;            
    }

    public async Task Invoke(HttpContext context, ReportScopeContext scopeContext)
    {        
      var controllerActionDescriptor = context
        .GetEndpoint()
        .Metadata
        .GetMetadata<ControllerActionDescriptor>();

      bool analytical = controllerActionDescriptor.EndpointMetadata.Any(m => m is ReportScopeAttribute);
      if (analytical) scopeContext.SetActive();
      
      await _next(context);
    }       
  }

In this middleware I use ReportScopeContext.

  public class ReportScopeContext
  {
    public bool Active { get; private set; } = false;
    public void SetActive()
    {
      Active = true;
    }
    
  }

This ReportScopeContext has scoped lifetime in DI and I use it to select an implementation of IService:

  services.AddScoped<ReportScopeContext>();
  services.AddTransient<Service2>();
  services.AddTransient<Service1>();
  services.AddTransient<IService>(sp =>
    sp.GetRequiredService<ReportScopeContext>().Active
      ? sp.GetRequiredService<Service1>()
      : sp.GetRequiredService<Service2>());
Agreement answered 20/9, 2022 at 9:10 Comment(0)
P
0

You can use reflection to find all the implementations in your assembly then register them. Here I use a WebApplicationBuilder to get an IServicesCollection which I then register the implementations against:

var myInterfaceTypes = typeof(SomeClassInTheAssembly).Assembly.GetTypes()
    .Where(t => typeof(IMyInterface).IsAssignableFrom(t) && !t.IsAbstract).ToList();

foreach (var t in myInterfaceTypes)
{
    builder.Services.AddTransient(typeof(IMyIterface), t);
}

// If we have a NeedsList class which needs a list of all the IMyInterface

builder.Services.AddTransient<INeedsList, NeedsList>(
ipt => new NeedsList(
    ipt.GetServices<IMyIterface>().ToList())
);
Paraformaldehyde answered 16/3, 2023 at 17:55 Comment(1)
I guess this does not address how you inject.. Maybe you can do that in the bottom bit when you instantiate the NeedsList ... Having to inject may point to you trying to 'get around' the interface by detecting the implementation class, which is good to avoid with say a strategy pattern or something.Paraformaldehyde
Z
0

We can solve this problem in an somewhat elegant way without anti-pattern and without modifying the existing service class.

Assume class MyService is where multiple instances are desired:

// we keep this class as is
public class MyService
{
   public MyService(/* deps */) {}
}

public class MyService<T> : MyService where T : IServiceKey
{
    public MyService(/* deps */) : base(/* deps */) {}
}


public interface IServiceKey {}
public abstract class A : IServiceKey {}
public abstract class B : IServiceKey {}

Then we can leverage the standard mechanism for Dependency Injection without any service locator pattern.

services
  .AddSingleton<MyService<A>>()
  .AddSingleton<MyService<B>>();
Zackaryzacks answered 7/9, 2023 at 23:48 Comment(0)
F
0

I use ActivatorUtilities.CreateInstance

0. Test
public static void Test() {
    var serviceProvider = new ServiceCollection()
      .AddSingleton<IServiceX, ServiceC>()
      .AddSingleton<ServiceA>()
      .AddSingleton<ServiceB>()
      .AddSingleton<IRepo, RepoA>()
      .AddSingleton<IPhase, PhaseA>()
      .AddSpecificSingleton<ControllerA, ServiceB>()
      .AddSpecificSingleton<ControllerB, ServiceA>()
      .AddSingleton<ControllerC>()
      .BuildServiceProvider()
    ;
    var controllerA = serviceProvider.GetRequiredService<ControllerA>();
    var controllerB = serviceProvider.GetRequiredService<ControllerB>();
    var controllerC = serviceProvider.GetRequiredService<ControllerC>();
    Debug.Assert(controllerA.ServiceX is ServiceB);
    Debug.Assert(controllerB.ServiceX is ServiceA);
    Debug.Assert(controllerC.ServiceX is ServiceC);

    var serviceX1 = (ServiceX)controllerA.ServiceX;
    var serviceX2 = (ServiceX)controllerB.ServiceX;
    Debug.Assert(ReferenceEquals(serviceX1.Repo, serviceX2.Repo));
}
1. Declare controller

ControllerX has dependency IServiceX

public abstract class ControllerX {
    public IServiceX ServiceX { get; }
    public IPhase ThePhase { get; }

    public ControllerX(IServiceX serviceX, IPhase phase) {
        (ServiceX, ThePhase) = (serviceX, phase);
    }
}

public class ControllerA : ControllerX {
    public ControllerA(IServiceX serviceX, IPhase phase) : base(serviceX, phase) { }
}

public class ControllerB : ControllerX {
    public ControllerB(IServiceX serviceX, IPhase phase) : base(serviceX, phase) { }
}
2. ServiceX

ServiceX has dependency IRepo

public interface IServiceX : IGuidId { }

public abstract class ServiceX : TypeAndId, IServiceX {
    public IRepo Repo { get; }

    public ServiceX(IRepo repo) {
        Repo = repo;
    }
}

public class ServiceA : ServiceX {
    public ServiceA(IRepo repo) : base(repo) { }
}

public class ServiceB : ServiceX {
    public ServiceB(IRepo repo) : base(repo) { }
}
3. Repo
public interface IRepo : IGuidId {
}

public class RepoA : TypeAndId, IRepo { }
public class RepoB : TypeAndId, IRepo { }

// phase
public interface IPhase : IGuidId { }

public class PhaseA : TypeAndId, IPhase { }
public class PhaseB : TypeAndId, IPhase { }
4. Service & Repo have a property Id
public interface IGuidId {
    string Id { get; }
}

public abstract class TypeAndId : IGuidId {
    protected TypeAndId() {
        Id = $"{GetType().Name}-{Guid.NewGuid().ToString()[..7]}";
    }
    public string Id { get; }
}
5. Extension method, to inject specific dependency
public static class InjectExtended {
    public static IServiceCollection AddSpecificSingleton<
        TService,
        TDependencyImplement>(
        this IServiceCollection services)
        where TService : class
        where TDependencyImplement : class
    {
        return services.AddSingleton<TService>(sp => {
            var service = sp.GetRequiredService<TDependencyImplement>();
            return (TService)ActivatorUtilities.CreateInstance(sp, typeof(TService), service);
        });
    }
}
Footy answered 7/10, 2023 at 5:49 Comment(0)
S
0
services.AddScoped<IService, Service1>()
                .AddScoped<IService, Service2>();

services.AddSingleton<IFactory, Factory>();

In your controller

 public class SomeController : ControllerBase
{
    private readonly IService iService;
    
    public SomeController (IFactory factory, IEnumerable<IService> 
    services)
    {
        iService = factory.create("someKey", services);//User.serviceKey()
    }

    [Route("health-check")]
    [HttpGet]
    public IActionResult get() 
    {
        return Ok("Success");
    }
 }

In your factory class:

public interface IFactory
{
    IService create(string key, IEnumerable<IService> services);
}

public class Factory : IFactory
{
    private static Dictionary<string, Type> _servicesList;
    
    public Factory()
    {
        if (_servicesList == null) 
        {
            _servicesList = new Dictionary<string, Type>();
            _servicesList.Add("key1", typeof(Service1));
            _servicesList.Add("key2", typeof(Service2));
        }
        
    }
    public IService create(string key, IEnumerable<IService> services)
    {
        return services.Where(s => s.GetType() == 
                   _servicesList.GetValueOrDefault(key)) .Single();
    }
}

Example service interface and implementations:

public interface IService{
    void doSomething();
}

public class Service1 : IService{
   public void doSomething(){
      //doing something....
   }
}

public class Service2 : IService{
   public void doSomething(){
      //doing something....
   }
Shiflett answered 11/11, 2023 at 6:55 Comment(0)
C
-1

FooA, FooB and FooC implements IFoo

Services Provider:

services.AddTransient<FooA>(); // Note that there is no interface
services.AddTransient<FooB>();
services.AddTransient<FooC>();

services.AddSingleton<Func<Type, IFoo>>(x => type =>
{
    return (IFoo)x.GetService(type);
});

Destination:

public class Test
{
    private readonly IFoo foo;

    public Test(Func<Type, IFoo> fooFactory)
    {
        foo = fooFactory(typeof(FooA));
    }

    ....

}

If you want to change the FooA to FooAMock for test purposes:

services.AddTransient<FooAMock>();

services.AddSingleton<Func<Type, IFoo>>(x => type =>
{
    if(type.Equals(typeof(FooA))
        return (IFoo)x.GetService(typeof(FooAMock));
    return null;
});
Clite answered 13/8, 2020 at 18:16 Comment(2)
Sorry Lukasz, this is pretty much the solution that Miguel added at the top, but is also considered an anti-pattern. :( I thought ut was generally a good idea too, until I thought about it.Melba
The reason it's an anti-pattern is that there's a DIFFERENCE between DI and IoC... Effectively the above DOES achieve DI but the issue is that the destination IS DETERMINING AT RUNTIME, what it wants to consume and that's what violates the IoC principle.Melba

© 2022 - 2024 — McMap. All rights reserved.