DEV Community

Hari R
Hari R

Posted on

I Used to Fear PROGRAM.CS, Until I Understood the DI Container Like This 🍝

For the longest time, opening the Program.cs file felt like opening the engine of a spaceship. All that talk of services.AddScoped, AddTransient, AddSingleton, and builder.Services gave me anxiety.

I used to ask my seniors about it randomly, but I think my brain rejected all of it until I had to write an API from scratch, and I had to see it more simply.

Let me take you on a little story.

🍽️ You’re Running a Restaurant (That’s Your App)

Imagine your app is a restaurant. You’re the manager. Every day, you need to serve dishes to customers (handle API requests, basically).

You’ve got:

  • 👨‍🍳 A chef
  • 🧑‍🍳 A helper
  • 🥶 A fridge full of ingredients
  • đźš› A supplier who brings ingredients
  • đź§ľ Customers who order food (clients calling your API)

You don’t want to personally micromanage who chops onions, who cooks the sauce, or who gets the tomatoes from the fridge. That’s chaos.

So you hire an assistant manager — her name is DotNetta.

DotNetta is your Dependency Injection Container.

đź§  What DotNetta (the DI container) Does

Before opening for business, you give DotNetta a set of rules:

🗂️ “Whenever someone asks for a chef, give them Gordon.”
🗂️ “If a chef needs vegetables, get them from the fridge.”
🗂️ “If the fridge is empty, call the supplier.”

This is what happens in your Program.cs:

services.AddScoped<IChef, GordonChef>();
services.AddSingleton<IFridge, WalkInFridge>();
services.AddTransient<ISupplier, FreshFarmSupplier>();
Enter fullscreen mode Exit fullscreen mode

You're not cooking anything yet. You're just telling DotNetta how to assemble the kitchen team when someone places an order.

🛎️ A Customer Walks In

Let’s say a customer orders spaghetti.

DotNetta steps in like a pro:

  • 👨‍🍳 She grabs Gordon (the chef)
  • 🍅 Gordon says: “I need tomatoes and pasta.”
  • 🥫 She checks the fridge
  • ❌ If something’s missing, she calls the supplier
  • âś… She hands everything over to Gordon
  • 🍝 Dish gets made and served

You didn’t do a thing — and yet everything worked perfectly.

💡 That’s What Happens When You Inject Dependencies

In your code, instead of doing:

var fridge = new Fridge();
var chef = new GordonChef(fridge);
var dish = chef.MakeSpaghetti();
Enter fullscreen mode Exit fullscreen mode

You just say:

public class KitchenService
{
    public KitchenService(IChef chef)
    {
        _chef = chef;
    }
}
Enter fullscreen mode Exit fullscreen mode

And DotNetta figures out the rest.
She knows who the chef is, what he needs, and how to get it all set up.

đź§± What Is a Service?

In real world:

A service is just someone who does something useful — like cook food, deliver ingredients, or clean the kitchen.

In code:

A service is any class or interface that performs some logic or functionality, and might need other things to do its job.

You register these services with DotNetta (your DI container) so she knows who to call when it’s time to work.

🔄 So What is IoC (Inversion of Control)?

Before DotNetta, you were in control of creating everything:

var repo = new UserRepo();
var service = new UserService(repo);
Enter fullscreen mode Exit fullscreen mode

You were the chef, the supplier, the manager — everything.

But with DotNetta:

You tell the system what you need, not how to get it.

That’s Inversion of Control — the control of object creation is inverted. You no longer control the wiring. The framework does it for you.

It’s like placing an order and having an invisible kitchen team handle everything behind the scenes.

🎯 Why This Matters

  • You write less code
  • You focus on what the class does, not how it's built
  • You can easily swap parts (mock services, test different implementations)
  • You stop fearing the Program.cs file because now you know it’s just DotNetta’s instruction sheet

đź’¬ Final Words

I used to stare at Program.cs like it was some mystical scroll.

But now I know — it's just a menu.
DotNetta (the DI container) reads it, builds the team, gets the ingredients, and serves the customer.

All I do is say, “Hey, I need a spaghetti,” and trust the kitchen knows what to do.

Once you start thinking like this, your fear turns into clarity — and your code?
It starts to feel like magic.

Top comments (2)

Collapse
 
rohit_ profile image
Rohit Rohit

hey the article was cool and understandable. But I have some doubts like
In DI, framework knows how to resolve dependencies, I understood like we can easily swap dependencies.
But how do we write less code in this?
"You focus on what the class does, not how it's built", didn't understood this part!

Context:
In my company when I joined, they were writing all code using static classes and just refer them using class names, I have started implementing DI in new projects just for the sake because asp.net core is built on top of it and is recommended approach.

Also I implement interfaces for every class, I don't if i am doing that right?
And you said like we call these classes as services because they do some job that's valid point, but since i develop worker services there i use services for the tasks that run in background and for other classes that interact with files/db/api's I call them under infrastructure folder and others in utility, is that correct?

Collapse
 
harishankarr7 profile image
Hari R

Hey Rohit, really appreciate your thoughtful questions—here are some quick answers:

Less code with DI?
You write less code because the DI container takes care of wiring dependencies for you. No need to manually new things all over your app—just register once in Program.cs, and inject where needed.

“Focus on what it does, not how it's built”
It means your class just declares what it needs (via interfaces), not how to create those dependencies. So you focus on the class’s logic, not construction.

Moving from static to DI?
Yes, you’re on the right track. Static classes are hard to test or replace. DI gives you flexibility and testability—perfect move for new projects.

Interfaces for every class?
Not always needed. A good rule: if a class “does this, and then does that,” or has multiple responsibilities, it might be time to extract parts and use interfaces—especially when you need to mock or swap implementations.

Folder structure: Services vs Infra vs Utility
Your naming is solid. Just one note—DB-related stuff is often placed in a separate Data or Infrastructure project under a Repositories folder, keeping things clean and testable.

Hope this helps
-Hari