Dependency Injection

codewithmukesh/dotnet-claude-kit/skills/dependency-injection

by codewithmukesh23300897f4d1No license754 starsListed Oct 8, 2026Updated Oct 8, 2026Repository updated 2 months ago

Dependency injection patterns for .NET 10. Covers service lifetimes, keyed services, the decorator pattern, factory pattern, and common DI pitfalls. Load this skill when registering services, resolving lifetime issues, designing service composition, or when the user mentions "DI", "dependency injection", "service registration", "AddScoped", "AddTransient", "AddSingleton", "keyed services", "decorator", "Scrutor", "IServiceCollection", or "captive dependency".

Instructions only

Dependency Injection

Core Principles

  1. Constructor injection is the default — Inject dependencies through the constructor (primary constructors make this clean). No service locator, no property injection.
  2. Match lifetimes carefully — A singleton must never depend on a scoped or transient service. This is the most common DI bug.
  3. Register interfaces, resolve interfaces — Register services.AddScoped<IOrderService, OrderService>(), not the concrete type.
  4. Keyed services for strategy pattern — .NET 8+ keyed services replace manual factory patterns for selecting between implementations.

Patterns

Keyed Services (.NET 8+)

Use keyed services to register and resolve multiple implementations of the same interface.

csharp
// Registrationbuilder.Services.AddKeyedScoped<INotificationService, EmailNotificationService>("email");builder.Services.AddKeyedScoped<INotificationService, SmsNotificationService>("sms");builder.Services.AddKeyedScoped<INotificationService, PushNotificationService>("push");
// Resolution via attributepublic class OrderHandler([FromKeyedServices("email")] INotificationService notifier){    public async Task Handle(CreateOrder.Command command, CancellationToken ct)    {        // ... create order        await notifier.SendAsync(notification, ct);    }}
// Resolution via IServiceProviderpublic class NotificationRouter(IServiceProvider provider){    public INotificationService GetService(string channel)    {        return provider.GetRequiredKeyedService<INotificationService>(channel);    }}

Decorator Pattern

csharp
// Base servicepublic interface IOrderService{    Task<Result<Order>> CreateAsync(CreateOrderRequest request, CancellationToken ct);}
public class OrderService(AppDbContext db, TimeProvider clock) : IOrderService{    public async Task<Result<Order>> CreateAsync(CreateOrderRequest request, CancellationToken ct)    {        var order = Order.Create(request, clock.GetUtcNow());        db.Orders.Add(order);        await db.SaveChangesAsync(ct);        return Result.Success(order);    }}
// Decorator — adds loggingpublic class LoggingOrderService(IOrderService inner, ILogger<LoggingOrderService> logger) : IOrderService{    public async Task<Result<Order>> CreateAsync(CreateOrderRequest request, CancellationToken ct)    {        logger.LogInformation("Creating order for customer {CustomerId}", request.CustomerId);        var result = await inner.CreateAsync(request, ct);        if (result.IsSuccess)            logger.LogInformation("Order {OrderId} created", result.Value.Id);        return result;    }}
// Registration with Scrutorbuilder.Services.AddScoped<IOrderService, OrderService>();builder.Services.Decorate<IOrderService, LoggingOrderService>();

Registration by Convention (Scrutor)

csharp
// Auto-register all services matching a conventionbuilder.Services.Scan(scan => scan    .FromAssemblyOf<Program>()    .AddClasses(classes => classes.AssignableTo<ITransientService>())    .AsImplementedInterfaces()    .WithTransientLifetime()    .AddClasses(classes => classes.AssignableTo<IScopedService>())    .AsImplementedInterfaces()    .WithScopedLifetime());

Factory Pattern

When you need runtime logic to select an implementation.

csharp
builder.Services.AddScoped<IPaymentProcessor>(sp =>{    var config = sp.GetRequiredService<IOptions<PaymentOptions>>().Value;    return config.Provider switch    {        "stripe" => ActivatorUtilities.CreateInstance<StripeProcessor>(sp),        "paypal" => ActivatorUtilities.CreateInstance<PayPalProcessor>(sp),        _ => throw new InvalidOperationException($"Unknown payment provider: {config.Provider}")    };});

Options Registration

csharp
// Bind configuration section to a strongly-typed options classbuilder.Services.AddOptions<JwtOptions>()    .BindConfiguration("Jwt")    .ValidateDataAnnotations()    .ValidateOnStart();
// Inject as IOptions<T>public class TokenService(IOptions<JwtOptions> options){    private readonly JwtOptions _jwt = options.Value;}

Anti-patterns

Don't Capture Scoped Services in Singletons

csharp
// BAD — DbContext is scoped, captured by singleton = memory leak + stale databuilder.Services.AddSingleton<OrderCache>(); // depends on AppDbContext
// GOOD — use IServiceScopeFactory in singletonpublic class OrderCache(IServiceScopeFactory scopeFactory){    public async Task<Order?> GetAsync(Guid id)    {        await using var scope = scopeFactory.CreateAsyncScope();        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();        return await db.Orders.FindAsync(id);    }}

Don't Register Everything as Singleton

csharp
// BAD — making a service singleton when it holds mutable statebuilder.Services.AddSingleton<OrderService>(); // has DbContext dependency
// GOOD — match the lifetime to the service's needsbuilder.Services.AddScoped<OrderService>();

Decision Guide

ScenarioRecommendation
Stateless serviceScoped (default) or Transient
Configuration / cacheSingleton
DbContextScoped (registered by AddDbContext)
Multiple implementationsKeyed services (strategy pattern)
Cross-cutting behaviorDecorator pattern
Convention-based registrationScrutor
Runtime implementation selectionFactory delegate
Audit existing registrationsget_di_registrations MCP tool — lifetimes, duplicates, captive-dependency risks in one call
Strongly-typed configAddOptions<T>().BindConfiguration()

Source and attribution

Source:codewithmukesh/dotnet-claude-kitinskills/dependency-injectionat commit2330089

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal

More from codewithmukesh/dotnet-claude-kit

Wrap Up

codewithmukesh

Captures end-of-session work, pending tasks and learnings into a handoff file, and reloads it at session start.

Productivity & Workflow754updated 2 months ago

Workflow Mastery

codewithmukesh

Claude Code workflow mastery for .NET developers. Covers parallel execution with git worktrees, plan mode strategy, verification loops, auto-formatting hooks, permission setup for dotnet CLI, prompting techniques, subagent patterns, and context discipline — token budget management, MCP-first navigation, lazy loading, and subagent isolation — all adapted for the .NET ecosystem. Load this skill when setting up Claude Code for a .NET project, optimizing workflows, running parallel sessions, when context is running low or sessions feel sluggish, when exploring a large codebase efficiently, or when the user mentions "productivity", "workflow", "parallel", "worktree", "plan mode", "permissions", "hooks", "10x", "setup Claude Code", "speed up development", "context", "tokens", "budget", "running out of context", "too many files", or "large codebase". Inspired by tips from Boris Cherny (creator of Claude Code) and the Anthropic team.

Awaiting classification754updated 2 months ago

Vertical Slice

codewithmukesh

Guides .NET developers in structuring applications with Vertical Slice Architecture, covering feature folders, endpoint grouping and handler patterns.

Software Development754updated 2 months ago

Testing

codewithmukesh

Testing strategy for .NET 10 applications. Covers xUnit v3, WebApplicationFactory for integration tests, Testcontainers for real database testing, Verify for snapshot testing, and the AAA pattern. Load this skill when writing tests, setting up test infrastructure, reviewing test coverage, or when the user mentions "test", "xUnit", "WebApplicationFactory", "Testcontainers", "integration test", "unit test", "bUnit", "snapshot test", "Verify", "test coverage", "AAA pattern", "WireMock", or "FakeTimeProvider".

Awaiting classification754updated 2 months ago

Tdd

codewithmukesh

Guided test-driven development workflow for .NET 10 using xUnit v3, WebApplicationFactory, Testcontainers, and Verify snapshots. Follows the strict red-green-refactor cycle. Use when: "TDD", "test-driven", "let's TDD this", "red green refactor", "write the test first", or when building a feature with clear acceptance criteria.

Awaiting classification754updated 2 months ago

Spec

codewithmukesh

Turns a vague feature idea into an agreed, persisted specification file through structured questioning rounds.

Productivity & Workflow754updated 2 months ago