microsoft-extensions-dependency-injection
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.
What this skill does
# 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
| Pattern | Use 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.AddSinRelated in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.