🔍 Introduction
As a new developer, you’ve probably come across a situation where one class creates and controls the behavior of another class, making it tightly coupled and hard to test or extend. This is where Dependency Injection (DI) and Inversion of Control (IoC) come to the rescue.
In this blog, we’ll break down these concepts in simple terms, walk through real-world examples, and implement them using .NET Core, including Entity Framework Core registration in the DI container. We’ll also tackle common follow-up interview questions at the end.
🧠 What is Dependency Injection (DI)?
Dependency Injection is a software design pattern that helps you achieve loose coupling between classes. Instead of a class creating its own dependencies, they are provided from the outside, typically via constructor parameters.
🔌 Think of DI as plugging in different components into a machine instead of soldering them permanently.
🔄 What is Inversion of Control (IoC)?
Inversion of Control is the broader concept behind Dependency Injection. It means handing over the control of object creation to a framework or container. In .NET Core, the built-in IoC container handles object creation, lifetime management, and injection for you.
🧠 IoC = “Don’t call me, I’ll call you.”
⚙️ Types of Dependency Injection in .NET Core
-
Constructor Injection ✅ (Most common)
-
Property Injection
-
Method Injection
We’ll focus on Constructor Injection here.
📦 Real-Time Example: Notification System
Scenario
We need a system to send email notifications. We’ll inject an EmailNotificationService
into a controller rather than creating it inside.
Step 1: Define the Interface
Step 2: Implement the Service
Step 3: Inject into Controller
Step 4: Register in Program.cs
🗃️ How Do You Register EF Core in the DI Container?
Entity Framework Core’s DbContext
is also a dependency and must be registered in the DI container:
Sample ApplicationDbContext
Registering EF Core in Program.cs
🧪 Unit Test Example with Moq
🖼️ Visual Diagram – DI with IoC Container
✅ Benefits of Dependency Injection and IoC
Feature | Benefit |
---|---|
🧩 Loose Coupling | Components depend on abstractions, not concrete classes. |
🧪 Testability | Easier to write unit tests with mocks/fakes. |
🔁 Flexibility | Swap implementations without changing dependent classes. |
🧼 Clean Code | Keeps classes focused and readable. |
🔍 Debuggability | Isolated testing simplifies debugging. |
❓ Interview Follow-Up Questions & Answers
🔹 Normal Questions
Q1: What is the difference between IoC and DI?
👉 IoC is the principle of delegating control to a container. DI is one implementation of IoC, where dependencies are provided from the outside.
Q2: Why is constructor injection preferred?
👉 It makes dependencies mandatory and explicit. It’s also easier for testing and ensures the object is always initialized properly.
Q3: What are the lifetimes of services in .NET Core?
-
Transient
: New instance every time. -
Scoped
: One instance per request. -
Singleton
: One instance for the app’s lifetime.
Q4: How do you register multiple implementations of the same interface?
You can inject IEnumerable<INotificationService>
to resolve both.
Q5: What is the default IoC container in .NET Core?
👉 Microsoft.Extensions.DependencyInjection is the built-in default IoC container.
🔹 Tricky Questions
Q6: Can you inject a service inside another injected service?
👉 Yes. The DI container supports nested dependencies and will resolve the full dependency tree.
Q7: What happens if you inject a scoped service into a singleton?
👉 It causes a runtime exception. Scoped services cannot be safely injected into singletons. You should use IServiceProvider
or a factory pattern instead.
Q8: How would you resolve dependencies dynamically at runtime?
👉 You can use IServiceProvider.GetService<T>()
or ActivatorUtilities
.
Q9: How can you replace a service at runtime (e.g., in integration tests)?
👉 Use the ConfigureTestServices()
method in WebApplicationFactory
or use Replace()
extension from Microsoft.Extensions.DependencyInjection.Abstractions
.
Q10: What if a dependency has its own dependencies?
👉 The IoC container recursively resolves all nested dependencies automatically.
🎯 Summary
-
Dependency Injection enables decoupled and testable designs.
-
IoC delegates object creation to a container.
-
.NET Core makes it easy with a built-in DI system.
-
EF Core registration and service lifetimes are crucial for robust application architecture.
📢 Final Thoughts
Mastering Dependency Injection and IoC is essential for modern .NET developers. It’s not just about patterns — it’s about building clean, flexible, testable, and maintainable systems.