DI Container Fundamentals
Understand inversion of control, service registration, and how the .NET DI container resolves dependencies.
DI Container Fundamentals is a free C# Academy lesson on CoddyKit. This is lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the C# Academy learning path, and your progress syncs across the web and the CoddyKit app. The C# Academy course includes 4 lessons in total.
What Is Dependency Injection?
Dependency Injection (DI) is a design pattern where a class receives its dependencies from an external source rather than creating them itself.
Instead of writing var repo = new UserRepository(); inside a class, the class declares what it needs and the DI container provides it.
Inversion of Control
DI is a concrete implementation of the Inversion of Control (IoC) principle: the control of creating dependencies is inverted from the dependent class to an external container.
This makes classes easier to test and replace.
Setting Up the .NET DI Container
In a .NET app, the DI container is configured via IServiceCollection in Program.cs. Services are registered and then resolved by the runtime.
var builder = WebApplication.CreateBuilder(args);
// Register services
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<UserService>();
var app = builder.Build();
app.Run();Defining an Interface & Implementation
DI works best when you program to interfaces. Define an interface for the service contract and a concrete class that implements it.
public interface IUserRepository
{
User? GetById(int id);
}
public class UserRepository : IUserRepository
{
public User? GetById(int id)
{
// fetch from database
return new User { Id = id, Name = "Alice" };
}
}Resolving Services via Constructor
The container automatically injects registered services into constructors. The class only needs to declare the interface it needs — no new keyword required.
public class UserService
{
private readonly IUserRepository _repo;
public UserService(IUserRepository repo)
{
_repo = repo;
}
public User? FindUser(int id) => _repo.GetById(id);
}Registering Multiple Services
You can register as many services as needed. Common extension methods are AddTransient, AddScoped, and AddSingleton — each defining a different lifetime.
builder.Services.AddSingleton<IConfig, AppConfig>();
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddTransient<IEmailService, SmtpEmailService>();
builder.Services.AddScoped<UserService>();Using IServiceProvider Directly
In rare cases you need to resolve services imperatively. IServiceProvider lets you call GetRequiredService<T>() to resolve a service, but prefer constructor injection in most scenarios.
// Anti-pattern (service locator) — use sparingly
public class ManualResolver
{
private readonly IServiceProvider _sp;
public ManualResolver(IServiceProvider sp) => _sp = sp;
public void DoWork()
{
var svc = _sp.GetRequiredService<UserService>();
svc.FindUser(1);
}
}Registering Open Generics
.NET DI supports open generic registrations. You can register IRepository<> once and it resolves to Repository<T> for any entity type.
builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
// Resolves as:
// IRepository<User> -> Repository<User>
// IRepository<Order> -> Repository<Order>Validation at Startup
With ValidateOnStart() or ValidateScopes = true, the container can detect misconfigured registrations (like capturing a Scoped service inside a Singleton) at application start instead of at runtime.
builder.Services.AddScoped<IMyService, MyService>();
// Detect scope violations at startup
builder.Host.UseDefaultServiceProvider(options =>
{
options.ValidateScopes = true;
options.ValidateOnBuild = true;
});Decorating Services
You can wrap a service with a decorator without modifying the original class. Register the inner service first, then register the decorator that wraps it.
builder.Services.AddScoped<IUserRepository, UserRepository>();
// Decorator pattern
builder.Services.Decorate<IUserRepository, CachedUserRepository>();
// Requires Scrutor NuGet packageReal-World: Minimal API with DI
In a Minimal API, services are injected directly into route handler parameters. The container resolves them automatically.
app.MapGet("/users/{id}", (int id, UserService svc) =>
{
var user = svc.FindUser(id);
return user is null ? Results.NotFound() : Results.Ok(user);
});Quick Check
Which method should you prefer for obtaining dependencies in most classes?
Recap: DI Container Fundamentals
Key takeaways:
- DI inverts dependency creation: the container builds objects, not the class itself
- Register services with AddTransient / AddScoped / AddSingleton
- Constructor injection is the standard and preferred approach
- Open generics, decorators, and ValidateOnBuild are powerful advanced features
Learn C# with an AI tutor — free
Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.
- Courses
- 93
- Lessons
- 346
Frequently Asked Questions
Is the “DI Container Fundamentals” lesson free?
Yes — the full text of “DI Container Fundamentals” is free to read here on the web. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the C# Academy course, upgrade to CoddyKit PRO. The C# Academy course includes 4 lessons in total.
What will I learn in “DI Container Fundamentals”?
Understand inversion of control, service registration, and how the .NET DI container resolves dependencies. You practise C# Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start C# Academy?
No prior experience is required. C# Academy on CoddyKit is structured for beginners through advanced learners, so you can start here or from the beginning and move at your own pace. This is lesson 1 of 4.
How long does the “DI Container Fundamentals” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this C# Academy lesson?
Yes. Every C# Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- DI Container Fundamentals
- Service Lifetimes: Transient, Scoped, Singleton
- Constructor Injection & Interfaces
- Factory & Options Patterns