Lesson 15 / 26

Dependency Injection Basics

Register an interface and its implementation and receive it in an endpoint.

Ask, do not create

Instead of writing new EmailSender() inside your code, you ask for an IEmailSender and the built-in container gives you one. This makes code easy to test because tests can pass a fake.

Register, resolve, use

You register services once at startup, and the container supplies them wherever they are requested.

Three steps: register a service, resolve it, use it.
Figure 5.1 — Register, resolve and use.

Register and inject

AddScoped registers the pair. The endpoint parameter of type IGreeter is injected automatically.

builder.Services.AddScoped<IGreeter, Greeter>();

var app = builder.Build();
app.MapGet("/hi/{name}", (string name, IGreeter g) => g.Greet(name));
app.Run();

public interface IGreeter { string Greet(string name); }
public class Greeter : IGreeter
{
    public string Greet(string name) => $"Hello, {name}!";
}

The canteen counter

You do not cook your own lunch; you tell the counter what you need. If the canteen changes its chef, you do not care. Interfaces play the same role between your code and its helpers.

Quick check: Why depend on an interface instead of a concrete class?

  • Interfaces are faster at runtime
  • It lets you swap implementations, such as a fake in tests
  • The compiler requires it
  • It removes the need for a container
Answer

It lets you swap implementations, such as a fake in tests — Depending on an abstraction lets you replace the implementation without changing the caller.