Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
aaronontheweb avatar

Dependency Injection Patterns

  • 482 installs
  • 1.1k repo stars
  • Updated July 3, 2026
  • aaronontheweb/dotnet-skills

dependency-injection-patterns is a Claude Code skill that organizes ASP.NET Core DI registrations into IServiceCollection Add{Feature}Services extension methods for clean Program.cs files and reusable test configuration

About

dependency-injection-patterns is a Claude Code skill from aaronontheweb/dotnet-skills by Aaron Stannard for structuring Microsoft.Extensions.DependencyInjection registrations in ASP.NET Core. Instead of hundreds of lines in Program.cs, the skill groups related services into Add{Feature}Services extension methods placed in {Feature}ServiceCollectionExtensions.cs beside each feature. Developers reach for it when production and test environments must share wiring via WebApplicationFactory, Akka.Hosting.TestKit, or standalone ServiceCollection overrides. Advanced patterns cover conditional registration, factory-based services, keyed services, layered composition, and Akka.NET actor scope management.

  • Scoped vs singleton vs transient guidance
  • ASP.NET Core registration patterns
  • Testable service composition
  • Modular library DI boundaries
  • Common lifetime pitfall fixes

Dependency Injection Patterns by the numbers

  • 482 all-time installs (skills.sh)
  • Ranked #41 of 153 .NET & C# skills by installs in the Skillselion catalog
  • Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/aaronontheweb/dotnet-skills --skill dependency-injection-patterns

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs482
repo stars1.1k
Last updatedJuly 3, 2026
Repositoryaaronontheweb/dotnet-skills

How do you organize ASP.NET Core dependency injection?

Implement correct .NET dependency injection lifetimes, registrations, and service composition when building ASP.NET Core APIs, workers, and modular SaaS backends.

Who is it for?

.NET developers with growing Program.cs registration lists who need composable Add* extensions shared between production and test hosts.

Skip if: Developers on minimal APIs with a handful of services who do not need layered DI extension composition or test factory reuse.

When should I use this skill?

Program.cs or Startup.cs accumulates dozens of IServiceCollection registrations that should move into feature extension methods.

What you get

Feature-scoped ServiceCollectionExtensions files, composable Add methods, and test-ready DI configuration

  • ServiceCollectionExtensions files
  • Refactored Program.cs
  • Test-ready DI composition

By the numbers

  • Uses Add{Feature}Services naming convention for feature registration groups
  • Supports WebApplicationFactory and Akka.Hosting.TestKit test reuse patterns

Files

SKILL.mdMarkdownGitHub ↗

Dependency Injection Patterns

When to Use This Skill

Use this skill when:

  • Organizing service registrations in ASP.NET Core applications
  • Avoiding massive Program.cs/Startup.cs files with hundreds of registrations
  • Making service configuration reusable between production and tests
  • Designing libraries that integrate with Microsoft.Extensions.DependencyInjection

Reference Files

  • advanced-patterns.md: Testing with DI extensions, Akka.NET actor scope management, conditional/factory/keyed registration patterns

---

The Problem

Without organization, Program.cs becomes unmanageable:

// BAD: 200+ lines of unorganized registrations
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<IOrderRepository, OrderRepository>();
builder.Services.AddScoped<IProductRepository, ProductRepository>();
builder.Services.AddScoped<IUserService, UserService>();
// ... 150 more lines ...

Problems: hard to find related registrations, no clear boundaries, can't reuse in tests, merge conflicts.

---

The Solution: Extension Method Composition

Group related registrations into extension methods:

// GOOD: Clean, composable Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddUserServices()
    .AddOrderServices()
    .AddEmailServices()
    .AddPaymentServices()
    .AddValidators();

var app = builder.Build();

---

Extension Method Pattern

Basic Structure

namespace MyApp.Users;

public static class UserServiceCollectionExtensions
{
    public static IServiceCollection AddUserServices(this IServiceCollection services)
    {
        services.AddScoped<IUserRepository, UserRepository>();
        services.AddScoped<IUserReadStore, UserReadStore>();
        services.AddScoped<IUserWriteStore, UserWriteStore>();
        services.AddScoped<IUserService, UserService>();
        services.AddScoped<IUserValidationService, UserValidationService>();

        return services;
    }
}

With Configuration

namespace MyApp.Email;

public static class EmailServiceCollectionExtensions
{
    public static IServiceCollection AddEmailServices(
        this IServiceCollection services,
        string configSectionName = "EmailSettings")
    {
        services.AddOptions<EmailOptions>()
            .BindConfiguration(configSectionName)
            .ValidateDataAnnotations()
            .ValidateOnStart();

        services.AddSingleton<IMjmlTemplateRenderer, MjmlTemplateRenderer>();
        services.AddSingleton<IEmailLinkGenerator, EmailLinkGenerator>();
        services.AddScoped<IUserEmailComposer, UserEmailComposer>();
        services.AddScoped<IEmailSender, SmtpEmailSender>();

        return services;
    }
}

---

File Organization

Place extension methods near the services they register:

src/
  MyApp.Api/
    Program.cs                    # Composes all Add* methods
  MyApp.Users/
    Services/
      UserService.cs
    UserServiceCollectionExtensions.cs   # AddUserServices()
  MyApp.Orders/
    OrderServiceCollectionExtensions.cs  # AddOrderServices()
  MyApp.Email/
    EmailServiceCollectionExtensions.cs  # AddEmailServices()

Convention: {Feature}ServiceCollectionExtensions.cs next to the feature's services.

---

Naming Conventions

PatternUse For
Add{Feature}Services()General feature registration
Add{Feature}()Short form when unambiguous
Configure{Feature}()When primarily setting options
Use{Feature}()Middleware (on IApplicationBuilder)

---

Testing Benefits

The Add* pattern lets you reuse production configuration in tests and only override what's different. Works with WebApplicationFactory, Akka.Hosting.TestKit, and standalone ServiceCollection.

See advanced-patterns.md for complete testing examples.

---

Layered Extensions

For larger applications, compose extensions hierarchically:

public static class AppServiceCollectionExtensions
{
    public static IServiceCollection AddAppServices(this IServiceCollection services)
    {
        return services
            .AddDomainServices()
            .AddInfrastructureServices()
            .AddApiServices();
    }
}

public static class DomainServiceCollectionExtensions
{
    public static IServiceCollection AddDomainServices(this IServiceCollection services)
    {
        return services
            .AddUserServices()
            .AddOrderServices()
            .AddProductServices();
    }
}

---

Akka.Hosting Integration

The same pattern works for Akka.NET actor configuration:

public static class OrderActorExtensions
{
    public static AkkaConfigurationBuilder AddOrderActors(
        this AkkaConfigurationBuilder builder)
    {
        return builder
            .WithActors((system, registry, resolver) =>
            {
                var orderProps = resolver.Props<OrderActor>();
                var orderRef = system.ActorOf(orderProps, "orders");
                registry.Register<OrderActor>(orderRef);
            });
    }
}

// Usage in Program.cs
builder.Services.AddAkka("MySystem", (builder, sp) =>
{
    builder
        .AddOrderActors()
        .AddInventoryActors()
        .AddNotificationActors();
});

See akka-hosting-actor-patterns skill for complete Akka.Hosting patterns.

---

Anti-Patterns

Don't: Register Everything in Program.cs

// BAD: Massive Program.cs with 200+ lines of registrations

Don't: Create Overly Generic Extensions

// BAD: Too vague, doesn't communicate what's registered
public static IServiceCollection AddServices(this IServiceCollection services) { ... }

Don't: Hide Important Configuration

// BAD: Buried settings
public static IServiceCollection AddDatabase(this IServiceCollection services)
{
    services.AddDbContext<AppDbContext>(options =>
        options.UseSqlServer("hardcoded-connection-string"));  // Hidden!
}

// GOOD: Accept configuration explicitly
public static IServiceCollection AddDatabase(
    this IServiceCollection services,
    string connectionString)
{
    services.AddDbContext<AppDbContext>(options =>
        options.UseSqlServer(connectionString));
}

---

Best Practices Summary

PracticeBenefit
Group related services into Add* methodsClean Program.cs, clear boundaries
Place extensions near the services they registerEasy to find and maintain
Return IServiceCollection for chainingFluent API
Accept configuration parametersFlexibility
Use consistent naming (Add{Feature}Services)Discoverability
Test by reusing production extensionsConfidence, less duplication

---

Lifetime Management

LifetimeUse WhenExamples
SingletonStateless, thread-safe, expensive to createConfiguration, HttpClient factories, caches
ScopedStateful per-request, database contextsDbContext, repositories, user context
TransientLightweight, stateful, cheap to createValidators, short-lived helpers
// SINGLETON: Stateless services, shared safely
services.AddSingleton<IMjmlTemplateRenderer, MjmlTemplateRenderer>();

// SCOPED: Database access, per-request state
services.AddScoped<IUserRepository, UserRepository>();

// TRANSIENT: Cheap, short-lived
services.AddTransient<CreateUserRequestValidator>();

Scoped services require a scope. ASP.NET Core creates one per HTTP request. In background services and actors, create scopes manually.

See advanced-patterns.md for actor scope management patterns.

---

Common Mistakes

Injecting Scoped into Singleton

// BAD: Singleton captures scoped service - stale DbContext!
public class CacheService  // Registered as Singleton
{
    private readonly IUserRepository _repo;  // Scoped - captured at startup!
}

// GOOD: Inject IServiceProvider, create scope per operation
public class CacheService
{
    private readonly IServiceProvider _serviceProvider;

    public async Task<User> GetUserAsync(string id)
    {
        using var scope = _serviceProvider.CreateScope();
        var repo = scope.ServiceProvider.GetRequiredService<IUserRepository>();
        return await repo.GetByIdAsync(id);
    }
}

No Scope in Background Work

// BAD: No scope for scoped services
public class BadBackgroundService : BackgroundService
{
    private readonly IOrderService _orderService;  // Scoped - will throw!
}

// GOOD: Create scope for each unit of work
public class GoodBackgroundService : BackgroundService
{
    private readonly IServiceScopeFactory _scopeFactory;

    protected override async Task ExecuteAsync(CancellationToken ct)
    {
        using var scope = _scopeFactory.CreateScope();
        var orderService = scope.ServiceProvider.GetRequiredService<IOrderService>();
        // ...
    }
}

---

Resources

  • Microsoft.Extensions.DependencyInjection: https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection
  • Akka.Hosting: https://github.com/akkadotnet/Akka.Hosting
  • Akka.DependencyInjection: https://getakka.net/articles/actors/dependency-injection.html
  • Options Pattern: See microsoft-extensions-configuration skill

Related skills

How it compares

Choose dependency-injection-patterns over generic .NET skills when Program.cs registration sprawl needs composable extension methods and test factory reuse.

FAQ

What problem does dependency-injection-patterns solve?

dependency-injection-patterns prevents massive Program.cs files by grouping related Microsoft.Extensions.DependencyInjection registrations into Add{Feature}Services extension methods. Each extension lives in {Feature}ServiceCollectionExtensions.cs next to the feature it configure

How does dependency-injection-patterns help testing?

dependency-injection-patterns lets tests reuse production Add* extension methods via WebApplicationFactory, Akka.Hosting.TestKit, or standalone ServiceCollection, overriding only external dependencies. Production and test hosts share the same composable registration graph.

What advanced patterns does the skill include?

dependency-injection-patterns documents conditional registration, factory-based services, keyed services, layered AddAppServices composition, and Akka.NET actor scope management. An advanced-patterns reference covers lifetime pitfalls and anti-patterns to avoid.

.NET & C#backend

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.