In C#, of course, many dependencies can be registered for DI with one short line via Microsoft.Extensions.DependencyInjection, especially when there is exactly one implementation for a interface. For example, suppose that Service1 depends on three other services, and one of those on two more:
_serviceCollection.AddTransient<IService1,Service1>();
_serviceCollection.AddTransient<IOtherService,OtherService>();
_serviceCollection.AddTransient<IAnotherOne,Service1>();
_serviceCollection.AddTransient<IService4,Service4>();
_serviceCollection.AddTransient<IService5,Service5>();
_serviceCollection.AddTransient<IService6WithAReallyLongName,Service6WithAReallyLongName>();
Is there a terser/easier way to register dependencies when I want only one (or a few) of them to be "special", perhaps specified with a factory method? A variation of the example here is as follows; I want to specify a special concrete type for one dependency of a service, and it likely has its own dependencies. In code called from Startup.cs, I believe I’m forced to specify all dependencies, and all dependencies my special dependency:
_serviceCollection.AddTransient<IService1>(x =>
new Service1(
x.GetRequiredService<IOtherService>(),
x.GetRequiredService<IAnotherOne>(),
new SpecialService4(
x.GetRequiredService<IService5>(),
x.GetRequiredService<IService6WithAReallyLongName>() ) ));
(Of course, it could equally well be AddSingleton or AddScoped as well.)
Note in the above, I want only SpecialService to be the special concrete dependency that I am registering; the other five can take the default implementation as registered in the top code example.
That’s really a lot of picky error-prone code entry, and every time I explain it to one of my teammates they rightly lament and wail. I just want to override one thing, why do I have to override six things to do so?
I’m looking for something instead like the following hypothetical syntax:
_serviceCollection.AddTransient<IService1, Service1>()
.WithParameter<IService4, SpecialService4>();
And then have the constructors automatically fill in the concretes for IOtherService
, IAnotherOne
, IService5
, and IService6WithAReallyLongName
automatically.
Is there any better way, short of going to Autofac or some other DI container library, to specify one special concrete dependency for one parameter without specifying all other dependencies’ parameters AND all the non-special parameters of the special concrete dependency? Perhaps there is a NuGet package that adds some syntactic sugar to registering dependencies with AddTransient/AddScoped/AddSingleton?
It seems like this would happen all the time once we have more than one concrete class for an interface, so I’m surprised this need isn’t more prevalent.
4
Answers
You can use marker interfaces where
SpecialService1
has a dependency onIDependency1
but implemented in a "special" way bySpecialDependency1
:You need to add the following services to the container:
This is nowhere close to what you have described as your proposed solution but I believe that it solves your problem. Personally, and without knowing your use case, I would probably be in the "lament and wail" camp if I were to work with something like this.
One way to clean up your DI code somewhat is to register the special class as itself (or with a marker interface if you insist on only registering interfaces). Then register your factory delegate for the consumer, but use the DI container to construct the special class. Like so:
Edit
This has been nagging at me and I finally realized why. I originally saw this technique in the context of registering decorators. The
ActivatorUtilities.CreateInstance
method can be used to simplify this like so:The basic idea behind
ActivatorUtilities.CreateInstance
is that you can supply implementations of some or all of the constructor parameters, and anything that is not supplied will be retrieved from the service provider.Edit 2
Since this is something I’ve occasionally considered using myself, I created a package that is basically a facade over
ActivatorUtilities.CreateInstance
:https://www.nuget.org/packages/ServiceProviderContextualBinding/
Usage example:
There is another package that does something similar, but also includes assembly scanning:
https://www.nuget.org/packages/ForEvolve.DependencyInjection.ContextualBindings/
You can do something similar using keyed registrations. Unfortunately build in DI container does not provide such feature but you can use 3rd party IoC container to replace a build in one. For example Autofac has keyed service registration/resolution.
What you’re asking for is called Contextual Binding, and it’s a feature that some more advanced Dependency Injection frameworks have made available, at the cost of performance and simplicity. Microsoft.Extensions.DependencyInjection doesn’t support Contextual Binding.
You can find some examples of workarounds in the answers to this similar question.
One strategy that can at least simplify your current approach a little is to create a registration for the concrete type, and leverage that to avoid tightly coupling your service factory to that type’s dependencies: