The DI container
“What is dependency injection, and what does the DI container do?”
The part of .NET that builds your objects, and hands them to the classes that need them.
See it
In plain words
Your classes stop building their own parts. Each one only lists what it needs, in its constructor. When the app runs, the DI container, which is part of .NET, reads that list, builds every part first, then builds your class and hands the parts in. That is dependency injection. To swap a part, you change one line in Program.cs, not every class that uses it.
- Dependency injection means a class receives its dependencies instead of creating them, usually through its constructor: constructor injection.
- It is a form of inversion of control.
- In ASP.NET Core the built-in container () does the wiring: for each service it picks the constructor, recursively builds every dependency, which is the object graph, then passes them in, and it also manages each object's lifetime and disposal.
- Classes depend on interfaces, so they stay and can be tested with fakes.
In the lending API
The controller before and after, the service whose constructor the container reads, the registrations, and a test with no container at all. Press Next line to walk through them.
Lending.sln · where these 5 files sit
- src/
- Api/
- Controllers/Servicing/
- Controllers/Servicing/
- Application/Servicing/Loans/
- Api/
- tests/Application.UnitTests/Servicing/Loans/
What they ask next
Interviewers check that you can explain every line. Tap a question to open it.
Why not…Why not just call in the controller?
Three costs. The controller must know every class and every part of every part. Swapping one, say a database repository or a fake in a test, means editing every class that wrote . And hides a lifetime choice: here a new controller per request meant a new, empty repository. With DI, Program.cs makes that choice once, in the open.
ReportedWhat does IoC stand for, and why does it matter?
Inversion of control. Normally your class controls what it creates. With IoC that control moves out: something else creates the objects and hands them in, or calls your code. DI is one form of it. It matters because a class that creates nothing can be given any part that keeps the contract.
ReportedHow does DI work for controllers? When is the controller created?
For every , once the URL is matched to an action, ASP.NET Core creates the controller and asks the container for each constructor parameter. Controllers are not registered as services themselves, so a missing dependency of a controller only shows up when a request reaches it.
So does startup validation catch a controller's missing part?
No. Verified on .NET 10: the startup check covers registered services only. The app starts; that request returns 500. See .
Does a missing registration fail at startup or at the request?
Verified: in Development at startup, in Production at the first request that needs it (500). How to make it fail at startup everywhere: .
ReportedWhat are the different ways to inject a service?
Constructor injection is the normal one. An action method can also take services as parameters;
[FromServices]marks them, though ASP.NET Core usually infers it. Property injection is not supported by the built-in container. A class can also ask the container itself with : that is the service locator pattern, which hides what a class needs; see .ReportedA constructor needs too many dependencies. Which SOLID principle is broken?
Usually single responsibility: the class does too many jobs, and each job brings its own parts. Split it into smaller services. The container will happily build a class with ten parts, so the long constructor is your warning sign, not an error.
ReportedWhy is preferred with dependency injection?
A
private readonlyfield can only be set in the constructor, so nobody can swap a dependency halfway through the object's life. Primary constructor parameters, likeloanshere, are not read-only; a team that cares copies them intoprivate readonlyfields.ReportedHow was DI different in .NET Framework?
Old ASP.NET had no built-in container: teams added Unity, Autofac, Ninject or similar. ASP.NET Core ships one (), and the framework itself is built on it.
The class has two constructors. Which one does the container use?
The one with the most parameters it can fill. Verified: with
(),(IBar)and(IBar, IFoo)and onlyIBarregistered, it used(IBar). If two equally long ones could both be filled, it throws. Services normally have exactly one public constructor.How long does the container keep each object?
The registration says: transient (new every time), scoped (one per request, see ) or singleton (one for the app). That is . Objects it built, it also disposes; see .
Traps
- Don't say
Dependency injection and the DI container are the same thing.SayDI is the pattern: parts come in through the constructor. The container is a tool that automates it. The unit test above does DI by hand, with no container. - Don't say
The container builds all my objects at startup.SayIt builds each one only when something asks for it. The startup check only confirms that each registered service could be built. - Don't say
The container is what makes my class testable.SayConstructor injection does. Because LoanService takes its parts in, a test can pass a fake repository and a fake clock.
AI tools now write the constructors and the Program.cs lines for you. A question reported in the AI-era research: "AI generated this snippet. How do you check it is correct?" For DI code the check is walking the object graph: for each constructor parameter, which registration answers it, which class gets built, and when. A suggested registration change that only makes an error go away fails that check.