I have services that are derived from 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 it at runtime based on some key?
I don't see any of the Add
Service method takes key
or name
parameter that typically used to distinguish the concrete implementation.
public void ConfigureServices(IServiceCollection services)
{
// How do I register services here of the same interface
}
public MyController:Controller
{
public void DoSomeThing(string key)
{
// How do get service based on key
}
}
Is the Factory pattern the only option here?
Update1
I have gone though the article here that shows how to use factory pattern to get service instances when we have multiple concreate implementation. However it is still not complete solution. when I call _serviceProvider.GetService()
method I cannot inject data into constructor. For example consider this example
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 appropriate connection string?
In Unity or any other IOC we can do that at the time of type registration. I can use IOption however that will require me to inject all settings, I cannot inject a particular connectionstring into the service.
Also note that I am trying to avoid using other containers (including Unity) because then I have to register everything else ( eg Controllers) with new container as well.
Also using factory pattern to create service instance is against DIP as factory increases the number of dependencies a client is forced to depend upon details here
So I think the default DI in ASP.NET core missing 2 things
1>Register instances using key
2>Inject static data into constructor during registration
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
SNSConfig
appsettings.json
SNS Factory
Now you can get the SNS service for the region that you want in your custom service or controller
My solution for what it's worth... considered switching to Castle Windsor as can't say I liked any of the solutions above. Sorry!!
Create your various implementations
Registration
Constructor and instance usage...
Another option is to use the extension method
GetServices
fromMicrosoft.Extensions.DependencyInjection
.Register your services as:
Then resolve with a little of Linq:
or
(assuming that
IService
has a string property called "Name")Make sure to have
using Microsoft.Extensions.DependencyInjection;
Update
AspNet 2.1 source:
GetServices
I did a simple workaround using
Func
when I found myself in this situation.And use it from any class registered with DI like:
UPDATE
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.
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:
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)
)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:
First, I agree with you that depending on new things like
IOptions
(and therefore on packageMicrosoft.Extensions.Options.ConfigurationExtensions
) is not a good idea. I've seen some discussing aboutIOptions
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:
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:
Container: