SyntaxStudy
Sign Up
C# Dependency Injection in .NET
C# Beginner 1 min read

Dependency Injection in .NET

The built-in .NET dependency injection container is a first-class feature of the platform, not a third-party add-on. Services are registered with one of three lifetimes: `Transient` (new instance every time), `Scoped` (one instance per HTTP request or DI scope), and `Singleton` (one instance for the application's lifetime). Choosing the wrong lifetime can cause bugs like captive dependency issues. Constructor injection is the most common form: the container inspects a class's constructor, resolves each parameter type from the registry, and passes them in automatically. This decouples classes from their dependencies and makes them easy to test with mock implementations. The `IOptions` pattern binds configuration sections (from appsettings.json, environment variables, etc.) to strongly-typed POCO classes. It supports `IOptions` (singleton), `IOptionsSnapshot` (scoped, re-read on each request), and `IOptionsMonitor` (notified of live changes).
Example
// Dependency Injection lifetimes and IOptions<T>

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

// --- Configuration options class ---
public class EmailSettings
{
    public string  Host     { get; set; } = "smtp.example.com";
    public int     Port     { get; set; } = 587;
    public bool    UseSsl   { get; set; } = true;
    public string  FromName { get; set; } = "App";
}

// --- Service abstractions ---
public interface IEmailSender { Task SendAsync(string to, string subject, string body); }
public interface INotifier    { Task NotifyAsync(string userId, string msg); }

// --- Concrete services ---
public class SmtpEmailSender : IEmailSender
{
    private readonly EmailSettings _cfg;
    public SmtpEmailSender(IOptions<EmailSettings> opts) => _cfg = opts.Value;

    public Task SendAsync(string to, string subject, string body)
    {
        Console.WriteLine($"SMTP [{_cfg.Host}:{_cfg.Port}] To={to} Subject={subject}");
        return Task.CompletedTask;
    }
}

public class UserNotifier : INotifier
{
    private readonly IEmailSender _email;
    public UserNotifier(IEmailSender email) => _email = email; // injected!

    public Task NotifyAsync(string userId, string msg) =>
        _email.SendAsync($"{userId}@example.com", "Notification", msg);
}

// --- Composition root ---
var services = new ServiceCollection();

services.Configure<EmailSettings>(opts => { opts.Host = "mail.myapp.com"; opts.Port = 465; });
services.AddSingleton<IEmailSender, SmtpEmailSender>();
services.AddTransient<INotifier,    UserNotifier>();

using var sp      = services.BuildServiceProvider();
using var scope   = sp.CreateScope();
var notifier = scope.ServiceProvider.GetRequiredService<INotifier>();
await notifier.NotifyAsync("alice", "Your order has shipped!");