Intermediate 22 min readModule: Module 6: Dependency Injection & Configuration Architecture in .NET
Dependency Injection, Service Lifetimes & Options Pattern
Build maintainable, testable software using .NET's built-in Dependency Injection container (IServiceCollection), service lifetimes (Transient, Scoped, Singleton), and strongly typed Options.
What You Will Learn in This Lesson
- The Inversion of Control (IoC) principle and Dependency Injection in .NET
- Service Lifetimes: `Transient` (every request), `Scoped` (per HTTP request), `Singleton` (application lifetime)
- The Captive Dependency anti-pattern and how to avoid it
- Strongly typed application configuration with `IOptions<T>` and `IOptionsSnapshot<T>`
Introduction & Core Concept
Modern .NET includes a high-performance, built-in Dependency Injection (DI) container at the heart of the framework. Every component in ASP.NET Core—controllers, middleware, database contexts, loggers, and background services—is registered in the DI container and resolved via constructor injection.
WHY DOES THIS MATTER IN THE REAL WORLD?
Hardcoding dependencies with 'new Service()' creates tightly coupled systems that cannot be unit-tested. Dependency Injection decouples implementations from interfaces, enabling modular architecture and seamless mocking.
Syntax & Structure
csharp
builder.Services.AddScoped<IUserRepository, SqlUserRepository>();builder.Services.Configure<DatabaseOptions>(builder.Configuration.GetSection("Database"));Configuring DI Container and Constructor Injection in .NET
csharpcsharp
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061// Dependency Injection and Options Pattern in .NET 9using System;// 1. Strongly-typed configuration optionspublic class PaymentGatewayOptions{public string ApiKey { get; set; } = "sk_live_default_key";public int TimeoutSeconds { get; set; } = 30;}// 2. Service interface and implementationpublic interface IPaymentService{void ProcessPayment(decimal amount);}public class StripePaymentService : IPaymentService{private readonly PaymentGatewayOptions _options;// Constructor Injection of optionspublic StripePaymentService(PaymentGatewayOptions options){_options = options;}public void ProcessPayment(decimal amount){Console.WriteLine($"[Stripe] Processed payment of USD {amount:N2} using API Key ending in '...{_options.ApiKey[^4..]}'");}}// 3. Controller consuming injected servicepublic class CheckoutController{private readonly IPaymentService _paymentService;public CheckoutController(IPaymentService paymentService){_paymentService = paymentService;}public void ExecuteCheckout(decimal cartTotal){Console.WriteLine("Executing checkout transaction...");_paymentService.ProcessPayment(cartTotal);}}public class Program{public static void Main(){// Demonstration of resolving configured dependency hierarchyvar config = new PaymentGatewayOptions { ApiKey = "sk_live_kwas_academy_secret_9981" };IPaymentService paymentService = new StripePaymentService(config);var controller = new CheckoutController(paymentService);controller.ExecuteCheckout(249.99m);}}
Line-by-Line Technical Breakdown
1The Captive Dependency Anti-Pattern: A Captive Dependency occurs when a service with a longer lifetime captures a service with a shorter lifetime (e.g., a `Singleton` service injecting a `Scoped` DbContext). The scoped DbContext is kept alive for the lifetime of the application, causing concurrency exceptions and memory leaks.
Try It Yourself (Interactive Editor)
Modify the code in real-time and click Run to test live browser output and console logs.
Intelligent Code Runner & Live Sandbox[CSHARP]
CSHARP SOURCE EDITOR
Interactive Live CodeCommon Mistakes & How to Avoid Them
#1: Registering Entity Framework DbContext as a Singleton service.
DbContext is not thread-safe. Registering it as a Singleton causes multiple concurrent HTTP requests to share the same database connection, causing fatal runtime concurrency crashes.
Incorrect / Antipattern
builder.Services.AddSingleton<AppDbContext>();Correct / Professional Solution
builder.Services.AddDbContext<AppDbContext>(); // Defaults to ScopedIndustry Best Practices & Professional Standards
- Register DbContexts and unit-of-work repositories as `Scoped` services.
- Register lightweight, stateless utility services as `Transient`.
- Register thread-safe caches and telemetry clients as `Singleton`.
Lesson Summary & Core Takeaways
- .NET features a built-in IoC container managing service lifetimes.
- `Transient`, `Scoped`, and `Singleton` control instance allocation.
- The Options pattern binds `appsettings.json` sections to strongly typed classes.