3 min read

How I Stopped Creating My Own Objects: Learning Dependency Injection in .NET

How I Stopped Creating My Own Objects: Learning Dependency Injection in .NET

During my early days of coding, I had a hard time understanding Dependency Injection (DI). I was so accustomed to creating objects directly inside my classes. Every time I needed something, I would simply instantiate it. For example:

public class UserService
{
  private readonly UserRepository _repo;

  public UserService()
  {
      _repo = new UserRepository(); // tightly coupled
  }
  
  public void RegisterUser(string name)
  {
      _repo.Save(name);
  }
}

At first, this looked completely fine. The code worked, the user was saved, and everything seemed in order.

However, as the project grew, the challenges began to show. Any slight change in UserRepository forced me to modify UserService.

Testing became difficult because I couldn’t easily replace or mock the repository. The code was starting to depend too heavily on specific implementations.
That’s when I came across the concept of Dependency Injection, and it changed how I approached software design.

What Is Dependency Injection?

Dependency Injection is a design pattern that allows a class to receive its dependencies from an external source rather than creating them internally. It’s part of the broader principle known as Inversion of Control (IoC) — where we let the framework or external code handle the creation and management of dependencies.

In simpler terms, instead of a class saying “I’ll create what I need,” it says “I’ll take what I’m given.”

Here’s the same example rewritten using DI:

public class UserService
{
  private readonly IUserRepository _repo;
  public UserService(IUserRepository repo)
  {
      _repo = repo; // injected from outside
  }
  
  public void RegisterUser(string name)
  {
      _repo.Save(name);
  }
}

Now, UserService no longer cares about how the data is saved or which repository implementation is used.
It just expects something that behaves like an IUserRepository.

💡
If you're reading about object creation and memory management in .NET but aren't familiar with C#, don't worry—I highly recommend checking out my course, C# Console and Windows Forms Development w/ Entity Framework.

Configuring Dependency Injection in .NET

In .NET, the DI system is built into the framework. You typically register your dependencies in the Program.cs or Startup.cs file like this:

builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<UserService>();

When the application runs, .NET automatically creates and injects the correct dependencies wherever they’re needed.
This is known as constructor injection, the most common and recommended form of DI in .NET.

💡
If you want to learn ASP.NET Core development - I highly recommend my course, Complete ASP.NET Core and Entity Framework Development. Great for those new to .NET Core.

Why Dependency Injection Matters

After adopting DI, I immediately noticed how much cleaner and more manageable the code became. Here’s why it makes such a difference:

  • Loose coupling: Classes depend on abstractions, not concrete implementations.
  • Easier testing: You can inject mock or fake implementations for testing purposes.
  • Better maintainability: Changing one part of the system has minimal impact on others.
  • Improved readability: The intent of each class becomes clearer when dependencies are explicit.

A Simple Real-Life Analogy
Imagine hiring a driver for your company. If the driver insists on bringing their own car, you’ll have little control if the vehicle breaks down, and work stops.
But if your company provides the car, you can replace or upgrade it whenever necessary, without changing the driver. The driver only needs to know how to operate a vehicle, not a specific one.
That’s the essence of Dependency Injection: your classes depend on interfaces or abstractions, not specific concrete objects.


In the Context of Clean Architecture

Clean Architecture, introduced by Robert C. Martin, emphasizes separating concerns between different layers of an application, such as entities, use cases, and infrastructure.

💡
Take your .NET skills to the next level with my in-depth course on Clean Architecture in .NET
Learn to create testable, maintainable apps using SOLID principles and clean architecture. Apply enterprise-grade design practices to your .NET projects.

Dependency Injection helps achieve that separation. It ensures that your core business logic doesn’t directly depend on external frameworks, databases, or APIs. Instead, those dependencies are provided from the outer layers, keeping your core clean and independent.


Final Thoughts

Dependency Injection can feel confusing at first, mainly when you’re used to creating objects directly. But once you grasp the concept, it becomes one of the most valuable tools for writing clean, scalable, and testable code.

If you’re new to DI in .NET, start small. Refactor a few classes to receive dependencies through their constructors, register them with the built-in container, and observe the difference in flexibility and testability.

Before long, you’ll find yourself writing less coupled and far more maintainable code, and you’ll never look back at those tightly bound new statements the same way again.


Dive Deeper — Learn Clean Architecture in .NET

If you’d like to explore how Dependency Injection fits into a full Clean Architecture setup, check out my detailed Udemy course here:

🎓 Clean Architecture in .NET — Full Practical Course

💡
Interested in learning more? Grab your copy of "Effective .NET Memory Management" here and take your .NET skills to the next level!