ASP.NET Core with Lamar : Solving the “single interface — multiple implementations” dependency injection problem
The reasons for writing this article is that I feel many new developers are overwhelmed by dependency injection and inversion of control, I know I was. Further more, I consider that experienced programmers will get a possibly fresh glimpse towards IoC containers, a pattern that they once mastered or avoid tackling it altogether, for various reasons. Others implement it mechanically yet chaotically in the sense references across namespaces/projects/assemblies “leak” into each other albeit there exists a container in place.
Thus I would like to popularize bits related to the DI, share my experience and my option towards it. Also I wanted to provide a working example project.
Most of what I cover can be found online, and most of the documentations are solid, but sometimes there is the case of putting together the pieces from several sources and that is what I try to do here.
Of course there are some of you who will, after reading this, say “Duh!…” or consider this a re-iteration of an long debated problem, but that proves there is more than a bunch of knowledgeable developers out there.
Depending on the feedback, but not only, I intend to continue writing the solution to the same issue using other popular containers.
For the record, I do not claim to be an expert in anything, but more as a person experiencing moments of passion towards software development.
Setup and usual usage
We start by executing
~$ dotnet new webapi and creating a
StudentsController which has a
Get method returning, for simplicity, only the names of the students. These values will be formated by a formatter that concatenates the first name and the last name.
I also added the formatter as a transient services in ASP.NET Core’s default DI implementation in
The project compiles and runs as expected.
The second formatter
So, let’s go to what this article is all about. Imagine you have to provide a second formatter via the same interface in the same class. You have to access different functionality abstracted behind the same API.
FancyFormatter just writes the last name first, followed by a comma.
Now if we just register it again as a service in
Startup.csit will replace the basic one.
The easiest, non black magic, solution is to use another interface that inherits from
IFormatter. The code snipped shows the key parts that need to be altered.
Cool, done! End of story! Not really…
Note: The example here is a little forced, in the sense that we already use the second formatter that is passed here as a being the fancy one, meaning we know its implementation, so it is safe maybe to have an intermediary, different, more revealing interface type.
Yet maybe you change your mind on what is fancy and want to swap the two. Would be a shame to temper with the code, except the DI registration. Furthermore, consider when a library needs two of the same types or consider a new
ProfessorsController that requires a
IFormatter, and the concrete type should be decided only at startup.
We undo the constructor to the previous signature.
Lamar is the successor to StructureMap, the ultra popular IoC container. It has almost the same API, thus the StuctureMap disciples amongst you might already know what comes next.
Lamar.Microsoft.DependencyInjection exposes the extension method
UseLamar for the
IWebHostBuilder, which needs to be called.
Startup.cs there are 3 essential changes to be made.
First change is to remove the
ConfigureServices method with a different,
Secondly, allow the service registration to treat controllers as services, opening the possibility to configure them explicitly , by calling
Third, use Lamar’s power to point each implementation of the
IFormatter interface to the desired argument, by name demarcation .
The other strong use case of specifying constructor dependencies, mentioned above, is when another class that would require the same interface, yet the functionality could defer. (Aaand when injecting stuff into third-party code, of course.)
At this point the
BasicFormatter will get injected, as it is the default option. This can be changed with the lines below. Notice here that there is no need to pass in an argument name because there is just one argument required (or of that type) in the constructor.
- Strong decoupling of application specific classes from the application bootstrapping approach.
- A documented standard, no “in house” workarounds or management
- No attributes (hinting to a container library) in app code.
- No service locator (anti) pattern.
- Extra (manageable) complexity on the registration part.
- Awareness of argument names.
- A new library with a new API
I remember when bumped into this issue and wouldn’t have the patience to properly read a documentation. I tough I would implement it myself, have a chance to flex my thought to have skills, but many, if not most, answers are already out there.
A .NET guru, whose name sadly I can’t remember but maybe I’ll edit it in later, once said not to abstract over the container because you’ll lose its power. The consecrated libraries are stable and having an unexpected breaking change it is rarely the case anymore.
This article presented a way to avoid a “hard” dependency on the DI library with all calls/references to that library residing in the start-up/bootstrapping phase. Then you can easily turn to another library or revert to none at all.
All code can be found at:
GitHub is where people build software. More than 28 million people use GitHub to discover, fork, and contribute to over…github.com
Learn how ASP.NET Core implements dependency injection and how to use it.docs.microsoft.com
Part of Lamar's mission is to be a much more performant replacement for the venerable StructureMap IoC container…jasperfx.github.io