---
description: C# Dependency Injection
alwaysApply: false
---

# C# Dependency Injection

The built-in `Microsoft.Extensions.DependencyInjection` container is the standard.

## Service Lifetimes

- **Transient** — new instance every time; lightweight, stateless services
- **Scoped** — one per request; DbContext, Unit of Work
- **Singleton** — one for app lifetime; caches, config, HTTP clients

## Captive Dependency Problem

A singleton must NEVER depend on a scoped/transient service — it captures a stale instance.

```csharp
// WRONG: singleton captures scoped DbContext
public class CachedService(AppDbContext db) { } // Singleton with scoped dep!

// RIGHT: use IServiceScopeFactory
public class CachedService(IServiceScopeFactory scopeFactory) {
    public async Task<User?> GetAsync(int id) {
        using var scope = scopeFactory.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        return await db.Users.FindAsync(id);
    }
}
```

## Registration Patterns

```csharp
// Extension methods for clean composition root
public static class OrderServiceExtensions {
    public static IServiceCollection AddOrderServices(this IServiceCollection s) {
        s.AddScoped<IOrderRepository, OrderRepository>();
        s.AddScoped<IOrderService, OrderService>();
        return s;
    }
}
// Program.cs: builder.Services.AddOrderServices();
```

## Options Pattern

```csharp
builder.Services
    .AddOptions<SmtpOptions>()
    .BindConfiguration(SmtpOptions.SectionName)
    .ValidateDataAnnotations()
    .ValidateOnStart();
// Inject IOptions<T> for static, IOptionsMonitor<T> for reloadable
```

## Keyed Services (.NET 8+)

```csharp
builder.Services.AddKeyedSingleton<INotifier, EmailNotifier>("email");
builder.Services.AddKeyedSingleton<INotifier, SmsNotifier>("sms");
// Inject: ([FromKeyedServices("email")] INotifier notifier)
```

## Interface Segregation

- Split fat interfaces into focused ones (e.g., `IUserReader` / `IUserWriter`)
- Consumers only depend on what they use

## Anti-Patterns

- **Service locator** — injecting `IServiceProvider` hides dependencies; use constructor injection
- **Concrete types without interfaces** — can't mock in tests; register with interface
- **Static service accessors** — global mutable state; use DI properly
