Named injected dependencies in .NET 5 / Core
At ahead intranet we like to keep the number of moving parts low in our code. This would also mean to come back to the default dependency injection container that comes with .NET Core / .NET 5 applications (right now we’re using Autofac). So far, however, we are using some container features that have no direct translation to the default one, most notably having named or keyed dependencies.
In some research I came across Steve Collins’ blog post on “Getting named dependencies by Name or Key using the .NET Core Container”
There he shows how to inject a named delegate of the form
go and have a read!
Attempt No 1
Yesterday I tried to adapt his approach into a more generic fashion in that, given the following API:
It would not only register all implementations of IDependency
but register the referenced delegate which could then be injected and used in the code to get access to IDependency
instances by name. The idea was to compile a switch expression that would perform the lookup that Steve coded manually in the blog post. Alas, what looks so “easy” in the post actually involves a closure over a IServiceProvider
instance. You might recall that the C# compiler does plenty of things to support closures. This added difficulty was doing my head in and I really didn’t want to start recreating compiler code, so I gave up on it and was thrown back to good ol’ classes.
Attempt No 2
Introducing an interface like this:
It is possible to implement this interface, where the implementation has a dependency on IServiceProvider
- there is our closure 😅.
The AddAll
code already already knows what IDependency
are available. Providing the optional naming function triggers it to create a dictionary of names against concrete implementations:
Then the code will also register all concrete implementations of IDependency
. The implementation of INamedLookup
can now use the stored dictionary to instantiate concrete implementations based on the provided name:
This named lookup type is also registered in the container against the INamedLookup<T>
interface.
Any classes can now take a dependency on INamedLookup<IDependency>
and access named dependencies like so:
The relevant interface is still easy enough to mock in some test should you wish to do so. Finally, it is also straightforward to find its usage in the code, which was an excellent point being made in Steve’s post.
Addendum
This blog post contains quite some hand-waiving. If you’re interested in the concrete code of my proof of concept, check out this gist: