Day 11: Dependency Injection (DI)
Welcome to Day 11. One of the most powerful features built directly into ASP.NET Core from the ground up is Dependency Injection (DI).
The Problem
Imagine an OrderController that needs to save data. You could write:
public class OrderController
{
private DatabaseContext _db = new DatabaseContext(); // BAD!
public void SaveOrder()
{
_db.Save();
}
}
This is tight coupling. What if DatabaseContext needs a connection string? Now OrderController has to know the connection string. What if you want to swap the database out for a Mock testing database? You can’t.
The Solution: Inversion of Control
Don’t let the controller create its own dependencies. Let the framework give (inject) the dependencies into the controller via its constructor!
First, we create an interface:
public interface IEmailService
{
void SendEmail(string to, string body);
}
// And the implementation
public class SendGridEmailService : IEmailService
{
public void SendEmail(string to, string body)
{
Console.WriteLine($"Sending email to {to} via SendGrid...");
}
}
Step 1: Register the Service
In your Program.cs, before builder.Build(), you must teach the DI Container about your service.
var builder = WebApplication.CreateBuilder(args);
// "Hey Builder, whenever anyone asks for an IEmailService, give them a SendGridEmailService!"
builder.Services.AddScoped<IEmailService, SendGridEmailService>();
var app = builder.Build();
Service Lifetimes
There are three main lifetimes you can register a service with:
AddTransient- A totally new instance is created every single time someone asks for it.AddScoped- A single instance is created per HTTP Request. (Excellent for Databases!)AddSingleton- One instance is created the first time it’s needed, and every single class shares that exact same instance forever until the server shuts down. (Good for memory caches).
Step 2: Request the Service (Injection)
Now, any Minimal API Endpoint (or Controller) can just ask for it in the method parameters!
app.MapPost("/checkout", (IEmailService emailSvc) =>
{
// The framework magically provided emailSvc for us!
emailSvc.SendEmail("user@example.com", "Thanks for your order!");
return Results.Ok("Order placed.");
});
Because our endpoint depends on the Interface (IEmailService) and not the concrete implementation, we can easily swap out SendGrid for Mailgun in Program.cs without ever touching the endpoint code!
Challenge for Day 11
Create an IGreetingService with a string method GetGreeting(). Implement it with WelcomeService. Register it as a Singleton in builder.Services, and use it in an app.MapGet endpoint!
Tomorrow: Building your first Minimal API endpoints.