我有从同一个接口派生的服务.
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?
我没有看到任何带有 key 或 name 参数的 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),因为那样我还必须使用新容器注册其他所有内容(例如控制器).
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:
当我发现自己处于这种情况时,我使用 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 中注册同一接口的多个实现?
发布评论