0Pricing
C# Academy · Lesson

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 — 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, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

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 package

Real-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

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, and the C# Academy course includes 4 lessons in total. 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.

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; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.

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

  1. DI Container Fundamentals
  2. Service Lifetimes: Transient, Scoped, Singleton
  3. Constructor Injection & Interfaces
  4. Factory & Options Patterns
← Back to C# Academy