c# - 如何在 Asp.Net Core 中注册同一接口(interface)的多个实现?

标签 c# asp.net-core asp.net-core-mvc coreclr

我有从同一个接口(interface)派生的服务。

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

通常,其他 IoC 容器如 Unity允许您注册一些 Key 的具体实现这将它们区分开来。

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

我没有看到任何 Add采取 key 的服务方法或 name参数,通常用于区分具体实现。
    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?
       }
    }

工厂模式是这里唯一的选择吗?

更新 1
我已经看过文章 here这展示了当我们有多个具体实现时如何使用工厂模式来获取服务实例。然而,它仍然不是一个完整的解决方案。当我拨打 _serviceProvider.GetService()方法,我无法将数据注入(inject)构造函数。

例如考虑这个:
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()注入(inject)适当的连接字符串?
在 Unity 或任何其他 IoC 库中,我们可以在类型注册时做到这一点。我可以用 IOption ,但是,这将需要我注入(inject)所有设置。我无法将特定的连接字符串注入(inject)服务。

另请注意,我试图避免使用其他容器(包括 Unity),因为这样我还必须使用新容器注册其他所有内容(例如, Controller )。

此外,使用工厂模式创建服务实例是反对 DIP 的,因为它增加了客户端的依赖项数量 details here .

所以,我认为 ASP.NET Core 中的默认 DI 缺少两件事:
  • 使用 key 注册实例的能力
  • 在注册期间将静态数据注入(inject)构造函数的能力
  • 最佳答案

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

    首先声明一个共享委托(delegate):

    public delegate IService ServiceResolver(string key);
    

    然后在您的 Startup.cs ,设置多个具体注册和这些类型的手动映射:
    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 注册的类中使用它:
    public class Consumer
    {
        private readonly IService _aService;
    
        public Consumer(ServiceResolver serviceAccessor)
        {
            _aService = serviceAccessor("A");
        }
    
        public void UseServiceA()
        {
            _aService.DoTheThing();
        }
    }
    

    请记住,在这个例子中,解析的关键是一个字符串,为了简单起见,因为 OP 特别要求这种情况。

    但是您可以使用任何自定义分辨率类型作为键,因为您通常不希望一个巨大的 n-case 开关破坏您的代码。取决于您的应用程序的扩展方式。

    关于c# - 如何在 Asp.Net Core 中注册同一接口(interface)的多个实现?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52717148/

    相关文章:

    c# - 仅在绑定(bind)输入处更改焦点后设置属性

    c# - 在 C# 中为计时器使用 Graphics.DrawString 时如何减少闪烁?

    javascript - 通过http请求将列表从 Angular 传递到C#

    c# - FromHeaderAttribute 不适用于属性

    c# - 允许 "semi anonymous"认证

    C# UWP - 无法在同一解决方案中添加对项目的引用

    c# - 为什么基类必须有一个带 0 个参数的构造函数?

    c# - 为多个 HttpStatusCodes 设置一个 ProducesResponseType typeof

    mysql - ASP.NET Core 身份与 ClearDB MySql

    c# - 在 Controller 类之外获取 IRouter 实例的最简单方法