Dependency injection (DI) isn’t a new concept in the .NET space, but the ASP.NET team made it a mainstream feature of ASP.NET Core. ASP.NET Core ships with a default service location mechanism that may behave differently than the previous inversion of control (IoC) products.
In my opinion, one of the most powerful features of leveraging DI is the ability to create processing pipelines. In this post, we’ll see how we can build a straight-forward pipeline within ASP.NET, by registering multiple implementations in our services collection.
Services Collection
We can register our dependencies in the ConfigureServices
method in our Startup
class for those unfamiliar with ASP.NET. This method allows us to enroll implementations in the services collection and define an implementation’s lifetime scope. Additionally, we may see compound registration methods for frameworks like MVC, Razor Pages, and other technologies.
There are three kinds of lifetime scopes: Transient, Scoped, and Singleton. For those interested, read more about service lifetimes at Microsoft docs.
Registering An Interfaced Service
Let’s assume we have an HTTP API that echoes a user value back, enriched with trailing emojis. First, we’ll need an enriching interface.
Simple enough, we take in a string and mutate its value. Let’s implement a version of the interface that adds a smile (😀) emoji.
Let’s see how we might register the Smile
class into our service collection. We’ll use Singleton
as this class is stateless. Our first instinct might be to add the Smile
class as itself, hoping that ASP.NET would know what interfaces the class implements. This would be wrong.
We need to inform ASP.NET what we plan on accessing this implementation using the explicit type. In this case, we will access the Smile
class utilizing the IEmoji
interface.
Now, we can apply our IEmoji
interface to any constructor that requires its use.
This registration style works great for single implementation dependencies, but what happens when we have more than one?
Registering Multiple Services
We’re in luck! ASP.NET provides a mechanism for registering multiple implementations of a single interface. Let’s implement a few more instances of IEmoji
.
Remember how we needed to register our single implementation with the IEmoji
interface? Well, we need to do the same with all the new implementations.
Additionally, our class constructor needs to adapt to allow for multiple instances of IEmoji
.
Great! Here are some caveats that I found with registering and consuming multiple dependencies of the same interface type:
- The
IEnumerable
generic type must be the type the registered type withinConfigureServices
. - Registration order matters. The order of registration translates to the resolution of implementations. For example, our array order is
Smile
,Apple
, and thenThumbsUp
. - If no implementations are registered, ASP.NET will pass a
null
collection. Watch out!.
Let’s see it in action.
When we run our ASP.NET application with a query of ?value=Hello
, we get the following result.
Awesome!
The Possibilities
What advantages do we get from registering multiple implementations of the same interface? The most significant benefit is the ability to build processing pipelines. It’s how ASP.NET works! ASP.NET has many plugin locations, but utilizing a decoupled DI approach can reduce the ASP.NET Pipeline’s complexity in our custom pipelines.
In our simple demo, we were able to enrich our output with emojis. Each interface altered the previous IEmoji
value with more information. We could use the same approach to validate inputs, signal events to multiple processors, and log data to numerous targets. Building a custom pipeline also gives us the benefit of decoupling from ASP.NET, in the chance we want to host our solution in a non-web environment.
Conclusion
The ASP.NET DI infrastructure is robust for most common use cases, but some of its more advanced features can be difficult to see immediately. Multiple implementations registration and utilization is a handy feature once we understand how it works. The quirks are easy to follow, but personally wish that ASP.NET wouldn’t inject empty collections. We injected our dependencies into our ASP.NET controller in this post, but our dependencies are accessible anywhere within the ASP.NET pipeline. Access to the DI infrastructure allows us to take advantage of our dependencies in middleware, action filters, and input/output formatters. The sky is the limit!
I hope you enjoyed this post, and please let me know what kind of process pipelines you’re building in your applications.