wshaddix/dotnet-skills/microsoft-extensions-dependency-injection

Claude Code and agent skill by wshaddix/dotnet-skills

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Dependency injection patterns and best practices using Microsoft.Extensions.DependencyInjection for .NET applications. Use when configuring DI containers in .NET, choosing between service lifetimes (Singleton, Scoped, Transient), or implementing decorator patterns and service interception.

Install

npx vskill@latest install wshaddix/dotnet-skills/microsoft-extensions-dependency-injection

SKILL.md

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

The Problem

Without organization, Program.cs becomes unmanageable:

csharp
// 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>();
builder.Services.AddScoped<IOrderService, OrderService>();
builder.Services.AddScoped<IEmailSender, SmtpEmailSender>();
builder.Services.AddScoped<IEmailComposer, MjmlEmailComposer>();
builder.Services.AddSingleton<IEmailLinkGenerator, EmailLinkGenerator>();
builder.Services.AddScoped<IPaymentProcessor, StripePaymentProcessor>();
builder.Services.AddScoped<IInvoiceGenerator, InvoiceGenerator>();
// ... 150 more lines ...

Problems:

  • Hard to find related registrations
  • No clear boundaries between subsystems
  • Can't reuse configuration in tests
  • Merge conflicts in team settings
  • No encapsulation of internal dependencies

The Solution: Extension Method Composition

Group related registrations into extension methods:

csharp
// GOOD: Clean, composable Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddUserServices()
.AddOrderServices()
.AddEmailServices()
.AddPaymentServices()
.AddValidators();
var app = builder.Build();

Each Add* method encapsulates a cohesive set of registrations.


Extension Method Pattern

Basic Structure
csharp
namespace MyApp.Users;
public static class UserServiceCollectionExtensions
{
public static IServiceCollection AddUserServices(this IServiceCollection services)
{
// Repositories
services.AddScoped<IUserRepository, UserRepository>();
services.AddScoped<IUserReadStore, UserReadStore>();
services.AddScoped<IUserWriteStore, UserWriteStore>();
// Services
services.AddScoped<IUserService, UserService>();
services.AddScoped<IUserValidationService, UserValidationService>();
// Return for chaining
return services;
}
}
With Configuration
csharp
namespace MyApp.Email;
public static class EmailServiceCollectionExtensions
{
public static IServiceCollection AddEmailServices(
this IServiceCollection services,
string configSectionName = "EmailSettings")
{
// Bind configuration
services.AddOptions<EmailOptions>()
.BindConfiguration(configSectionName)
.ValidateDataAnnotations()
.ValidateOnStart();
// Register services
services.AddSingleton<IMjmlTemplateRenderer, MjmlTemplateRenderer>();
services.AddSingleton<IEmailLinkGenerator, EmailLinkGenerator>();
services.AddScoped<IUserEmailComposer, UserEmailComposer>();
services.AddScoped<IOrderEmailComposer, OrderEmailComposer>();
// SMTP client depends on environment
services.AddScoped<IEmailSender, SmtpEmailSender>();
return services;
}
}
With Dependencies on Other Extensions
csharp
namespace MyApp.Orders;
public static class OrderServiceCollectionExtensions
{
public static IServiceCollection AddOrderServices(this IServiceCollection services)
{
// This subsystem depends on email services
// Caller is responsible for calling AddEmailServices() first
// Or we can call it here if it's idempotent
services.AddScoped<IOrderRepository, OrderRepository>();
services.AddScoped<IOrderService, OrderService>();
services.AddScoped<IOrderEmailNotifier, OrderEmailNotifier>();
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
IUserService.cs
Repositories/
UserRepository.cs
UserServiceCollectionExtensions.cs # AddUserServices()
MyApp.Orders/
Services/
OrderService.cs
OrderServiceCollectionExtensions.cs # AddOrderServices()
MyApp.Email/
Composers/
UserEmailComposer.cs
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)
csharp
// Feature services
services.AddUserServices();
services.AddEmailServices();
services.AddPaymentServices();
// Third-party integrations
services.AddStripePayments();
services.AddSendGridEmail();
// Configuration-heavy
services.ConfigureAuthentication();
services.ConfigureAuthorization();

Testing Benefits

The main advantage: reuse production configuration in tests.

WebApplicationFactory
csharp
public class ApiTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly WebApplicationFactory<Program> _factory;
public ApiTests(WebApplicationFactory<Program> factory)
{
_factory = factory.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services =>
{
// Production services already registered via Add* methods
// Only override what's different for testing
// Replace email sender with test double
services.RemoveAll<IEmailSender>();
services.AddSingleton<IEmailSender, TestEmailSender>();
// Replace external payment processor
services.RemoveAll<IPaymentProcessor>();
services.AddSingleton<IPaymentProcessor, FakePaymentProcessor>();
});
});
}
[Fact]
public async Task CreateOrder_SendsConfirmationEmail()
{
var client = _factory.CreateClient();
var emailSender = _factory.Services.GetRequiredService<IEmailSender>() as TestEmailSender;
await client.PostAsJsonAsync("/api/orders", new CreateOrderRequest(...));
Assert.Single(emailSender!.SentEmails);
}
}
Akka.Hosting.TestKit
csharp
public class OrderActorSpecs : Akka.Hosting.TestKit.TestKit
{
protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider)
{
// Reuse production Akka configuration
builder.AddOrderActors();
}
protected override void ConfigureServices(IServiceCollection services)
{
// Reuse production service configuration
services.AddOrderServices();
// Override only external dependencies
services.RemoveAll<IPaymentProcessor>();
services.AddSingleton<IPaymentProcessor, FakePaymentProcessor>();
}
[Fact]
public async Task OrderActor_ProcessesPayment()
{
var orderActor = ActorRegistry.Get<OrderActor>();
orderActor.Tell(new ProcessOrder(orderId));
ExpectMsg<OrderProcessed>();
}
}
Standalone Unit Tests
csharp
public class UserServiceTests
{
private readonly ServiceProvider _provider;
public UserServiceTests()
{
var services = new ServiceCollection();
// Reuse production registrations
services.AddUserServices();
// Add test infrastructure
services.AddSingleton<IUserRepository, InMemoryUserRepository>();
_provider = services.BuildServiceProvider();
}
[Fact]
public async Task CreateUser_ValidData_Succeeds()
{
var service = _provider.GetRequiredService<IUserService>();
var result = await service.CreateUserAsync(new CreateUserRequest(...));
Assert.True(result.IsSuccess);
}
}

Layered Extensions

For larger applications, compose extensions hierarchically:

csharp
// Top-level: Everything the app needs
public static class AppServiceCollectionExtensions
{
public static IServiceCollection AddAppServices(this IServiceCollection services)
{
return services
.AddDomainServices()
.AddInfrastructureServices()
.AddApiServices();
}
}
// Domain layer
public static class DomainServiceCollectionExtensions
{
public static IServiceCollection AddDomainServices(this IServiceCollection services)
{
return services
.AddUserServices()
.AddOrderServices()
.AddProductServices();
}
}
// Infrastructure layer
public static class InfrastructureServiceCollectionExtensions
{
public static IServiceCollection AddInfrastructureServices(this IServiceCollection services)
{
return services
.AddEmailServices()
.AddPaymentServices()
.AddStorageServices();
}
}

Akka.Hosting Integration

The same pattern works for Akka.NET actor configuration:

csharp
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);
})
.WithShardRegion<OrderShardActor>(
typeName: "order-shard",
(system, registry, resolver) =>
entityId => resolver.Props<OrderShardActor>(entityId),
new OrderMessageExtractor(),
ShardOptions.Create());
}
}
// 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.


Common Patterns

Conditional Registration
csharp
public static IServiceCollection AddEmailServices(
this IServiceCollection services,
IHostEnvironment environment)
{
services.AddSingleton<IEmailComposer, MjmlEmailComposer>();
if (environment.IsDevelopment())
{
// Use Mailpit in development
services.AddSingleton<IEmailSender, MailpitEmailSender>();
}
else
{
// Use real SMTP in production
services.AddSingleton<IEmailSender, SmtpEmailSender>();
}
return services;
}
Factory-Based Registration
csharp
public static IServiceCollection AddPaymentServices(
this IServiceCollection services,
string configSection = "Stripe")
{
services.AddOptions<StripeOptions>()
.BindConfiguration(configSection)
.ValidateOnStart();
// Factory for complex initialization
services.AddSingleton<IPaymentProcessor>(sp =>
{
var options = sp.GetRequiredService<IOptions<StripeOptions>>().Value;
var logger = sp.GetRequiredService<ILogger<StripePaymentProcessor>>();
return new StripePaymentProcessor(options.ApiKey, options.WebhookSecret, logger);
});
return services;
}
Keyed Services (.NET 8+)

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
35
Repository forks
3
7-day trend score
18

Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 19 Mar 2026, 7 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/wshaddix/dotnet-skills/microsoft-extensions-dependency-injection/badge)](https://verified-skill.com/skills/wshaddix/dotnet-skills/microsoft-extensions-dependency-injection)

Something wrong with this skill or its page? Tell us.