Is there a easier way to register dependencies when I want only one of them to be "special"?

Solution 1:

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:

_serviceCollection.AddTransient<SpecialService4>();
_serviceCollection.AddTransient<IService1>(x => 
    new Service1(
       x.GetRequiredService<IOtherService>(),
       x.GetRequiredService<IAnotherOne>(), 
       x.GetRequiredService<SpecialService4>() ));

Solution 2:

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:

_serviceCollection.AddTransient<SpecialService4>();
_serviceCollection.AddTransient<IService1>(x => 
    new Service1(
       x.GetRequiredService<IOtherService>(),
       x.GetRequiredService<IAnotherOne>(), 
       x.GetRequiredService<SpecialService4>()));

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:

_serviceCollection.AddTransient<SpecialService4>();
_serviceCollection.AddTransient<IService1>(provider =>
    ActivatorUtilities.CreateInstance<Service1>(
        provider, 
        provider.GetRequiredService<SpecialService4>()));

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:

_serviceCollection.AddTransient<SpecialService4>();
_serviceCollection.WithReplacement<IService4, SpecialService4>()
    .AddSingleton<IService1, Service1>();

There is another package that does something similar, but also includes assembly scanning:

https://www.nuget.org/packages/ForEvolve.DependencyInjection.ContextualBindings/