Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
ASP.NET Core Basics: Pipeline, Services and Settings  ›  Lesson

Dependency Injection and Service Lifetimes

Framework Basics 16 minClasses ask for what they need in their constructor, the built-in container supplies it, and the registered lifetime decides whether they share one instance per app, per request, or none.

Dependency Injection and Service Lifetimes

LoanService needs a database context, a clock and a logger. If it created them with new, you could never swap the real clock for a fake one in a test, and every class would repeat the setup. ASP.NET Core is built around dependency injection instead: classes ask for what they need, and a container hands it over.

The rule

Ask in the constructor, register once, choose a lifetime: singleton, scoped, transient.

A class declares its dependencies as constructor parameters, usually interfaces:

public class LoanService(LibraryDb db, IClock clock)
{
    public async Task<Loan> BorrowAsync(int bookId, int memberId)
    {
        var loan = new Loan(bookId, memberId, due: clock.Today.AddDays(14));
        db.Loans.Add(loan);
        await db.SaveChangesAsync();
        return loan;
    }
}

Program.cs registers each service once with the built-in container, Microsoft.Extensions.DependencyInjection:

builder.Services.AddSingleton<IClock, SystemClock>();
builder.Services.AddDbContext<LibraryDb>(o => o.UseSqlite("Data Source=library.db"));
builder.Services.AddScoped<LoanService>();

When a controller, endpoint or other service asks for LoanService, the container builds it and fills in its constructor — recursively.

The lifetime says how often a new instance is made:

Lifetime New instance… Good for
Singleton once for the whole app stateless helpers, caches, config
Scoped once per HTTP request DbContext and services that use it
Transient every time it is asked for small, cheap, stateless objects

AddDbContext registers the context as scoped.

The shortcut

Apply one rule: a service may only depend on things that live at least as long as it does. Singleton can use singletons. Scoped can use singletons and scoped. Transient can use anything. A singleton that holds a scoped service keeps the first request's instance forever — a captive dependency.

Checking LoanService's registrationsStep 1 of 4
  1. List the lifetimes

    IClock singleton, LibraryDb scoped, LoanService scoped.

The trap: registering everything as a singleton "for speed". A DbContext is not thread-safe, and sharing one across concurrent requests corrupts its state.

Drills

Answer fast — trust the rule.

quick drill

How many LibraryDb instances does one HTTP request get when it is registered as scoped?

quick drill

Which lifetime creates a new instance every time the service is requested?

quick drill

Which registration creates a captive dependency?

quick drill

A stateless IClock that just returns the current time is best registered as what?

Remember

  • Ask in the constructor, register once, choose a lifetime.
  • Depend only on things that live at least as long as you.
  • DbContext is scoped; a singleton holding it is a captive dependency.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainPick the lifetimes

ReadingRoom has three services: LibraryDb (an EF Core DbContext), IClock (returns the current time, holds no state), and LoanService (uses LibraryDb). Choose a lifetime for each and explain why. Then explain what goes wrong if LoanService is registered as a singleton.