如何在Asp.Net Core中注册同一接口的多个实现? [英] How to register multiple implementations of the same interface in Asp.Net Core?

查看:606
本文介绍了如何在Asp.Net Core中注册同一接口的多个实现?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有从相同接口派生的服务.

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 { }

通常,其他IoC容器(例如Unity)允许您通过一些区分它们的Key注册具体的实现.

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

在ASP.NET Core中,如何注册这些服务并在运行时基于某些键解析它们?

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

我看不到任何带有keyname参数的Add服务方法,这些方法通常用于区分具体实现.

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?

更新1
我浏览了文章此处,该文章显示了如何使用工厂模式来获取服务有多个具体实现的实例.但是,它仍然不是一个完整的解决方案.当我调用_serviceProvider.GetService()方法时,无法将数据注入到构造函数中.

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.

例如考虑一下:

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;
    }
}

_serviceProvider.GetService()如何注入适当的连接字符串? 在Unity或任何其他IoC库中,我们可以在类型注册时执行此操作.我可以使用 IOption ,但是要求我注入所有设置.我无法在服务中注入特定的连接字符串.

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.

还请注意,我试图避免使用其他容器(包括Unity),因为那样的话,我还必须在新容器中注册其他所有内容(例如Controllers).

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.

此外,使用工厂模式创建服务实例也违反了DIP,因为它增加了客户端具有

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

因此,我认为ASP.NET Core中的默认DI缺少两件事:

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

  1. 使用密钥注册实例的能力
  2. 在注册过程中将静态数据注入构造函数的能力

推荐答案

当我发现自己处于这种情况时,我使用Func做了一个简单的解决方法.

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);

然后在您的Startup.cs中,设置多个具体的注册以及这些类型的手动映射:

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
    }
});

并在任何通过DI注册的类中使用它:

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();
    }
}

请记住,在此示例中,为简单起见,并且因为OP特别要求这种情况,所以解析的关键是字符串.

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.

但是您可以使用任何自定义解析类型作为键,因为通常不需要大的n-case开关使代码腐烂.取决于您的应用扩展方式.

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.

这篇关于如何在Asp.Net Core中注册同一接口的多个实现?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆