Dependency Injection in ASP.NET
Prologue
.NET supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies. Dependency injection in .NET is a built-in part of the framework, along with configuration, logging, and the options pattern.
In this article, I’ll talk about the basic use of DI in a .NET Core Web API project.
Further Explanation
Well, although the concept is easy to understand, but you may still feel confused when coding. For example, let’s take a look at Startup.cs
, in this file, you’ll see many interfaces, and yet you never find a place that these parameters are passed to Startup
class. How does it work? 😵💫
1 | public class Startup |
Well, this is how DI works, but it is more than just DI. Because here, DI is done by .NET framework automatically, so you don’t feel its presence.
Here, DI is done automatically, but it doesn’t mean that DI is always done automatically! For your own classes, you still have to manually resolve specific instances for injection.
Configure DI
Dependency Injection is used to achieve IoC, and Microsoft .NET framework already provided us a default implementation, which is the services
of type IServiceCollection
. Just like its name, it is just the container which holds all “things” to be resolved for injection. When we want to resolve things we’ve put into it, we’ll need its friend IServiceProvider
. We can get it by IServiceCollection.BuildServiceProvider()
or simply by another DI.
1 | var provider = services.BuildServiceProvider(); |
So, since this IServiceCollection
is a container, we should put things into it before we get it from it. Of course
As you may have get it before, DI simply get the corresponding implementation of the interface you provided. So what you add into it is actually a mapping from an interface to a concreate implementation class. DI will then create such instance for you when corresponding interface encountered.
There are three strategies for instantiation - transient, scoped and singleton. Easy to understand, transient instance will be created every time, scoped will only be created once every scope (might be a request), while singleton, you know, only one across the whole application. If you are concerned about thread safety, you’d better choose transient.
1 | // There are many overloads, here is only the common case |
After we registered mapping to the collection, we can then resolve them by its IServiceProvider
.
1 | IClass? IServiceProvider.GetService<IClass>(); |
Automatic DI
There are some common classes where DI will be applied automatically - Startup, Controllers and Services. We’ve seen Startup above, so here let’s have a look at Controllers and Services. Some properties and annotations are omitted to simplify the code.
1 | public class Startup |
Controllers are created by .NET framework, and the framework can automatically detect the parameters and use DI to inject correct instance to it. And the IRegisterService
here is what we registered in Startup.cs
, which will also be resolved automatically then.
There may be some other default interfaces for injection, but for now, I haven’t met them. So I guess it could be quite sufficient. 😉
Epilogue
Dependency Injection and Inverse of Control are design patterns, not just implementations. So the idea behind them is what matters most. 😌
I think this is already a good start off. Hope that I can excel later.