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
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 itIServiceAccessor
as an interface and then I had to add a some more extensions to theIServiceCollection
as such:The service Accessor looks like:
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:
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.
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:
Add a dependency to StructureMap in your
project.json
:Inject it into the ASP.NET pipeline inside
ConfigureServices
and register your classes (see docs)Then, to get a named instance, you will need to request the
IContainer
That's it.
For the example to build, you need