원클릭으로
dependency-injection
Use when registering services, choosing lifetimes, or implementing DI patterns like decorator or keyed services.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Use when registering services, choosing lifetimes, or implementing DI patterns like decorator or keyed services.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Use when adding caching to .NET APIs or optimizing response times with distributed cache, output cache, or ETags.
Use when configuring API response formats, custom formatters, or Accept header handling.
Use when building controller-based REST APIs with action results, model binding, or MediatR integration.
Use when creating RESTful API controllers with MediatR dispatch and ProblemDetails error responses.
Use when designing gRPC services, proto files, or adding gRPC-Web or JSON transcoding.
Use when building minimal API endpoints with route groups, filters, or TypedResults.
| name | dependency-injection |
| description | Use when registering services, choosing lifetimes, or implementing DI patterns like decorator or keyed services. |
| metadata | {"category":"core","agent":"dotnet-architect","alwaysApply":true} |
IServiceCollection extension methods// Organize by layer or feature
public static class DependencyInjection
{
public static IServiceCollection AddApplicationServices(
this IServiceCollection services)
{
services.AddMediatR(cfg =>
cfg.RegisterServicesFromAssembly(typeof(DependencyInjection).Assembly));
services.AddValidatorsFromAssembly(
typeof(DependencyInjection).Assembly);
return services;
}
public static IServiceCollection AddInfrastructureServices(
this IServiceCollection services, IConfiguration configuration)
{
// Scoped — one instance per HTTP request
services.AddScoped<IOrderRepository, OrderRepository>();
services.AddScoped<IUnitOfWork, UnitOfWork>();
// Open generics
services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>));
// Singleton — one instance for the app lifetime
services.AddSingleton<IDateTimeProvider, DateTimeProvider>();
// Transient — new instance every time
services.AddTransient<IEmailSender, SmtpEmailSender>();
return services;
}
}
// Program.cs
builder.Services
.AddApplicationServices()
.AddInfrastructureServices(builder.Configuration);
Singleton — stateless services, caches, configuration wrappers
Example: IDateTimeProvider, HybridCache, IOptions<T>
Warning: must be thread-safe
Scoped — per-request state, DbContext, UnitOfWork, repositories
Example: AppDbContext, ICurrentUserService
Default choice for most services
Transient — lightweight stateless services, factory output
Example: IEmailSender, validators
Warning: do not hold expensive resources
// Registration
services.AddKeyedSingleton<IPaymentGateway, StripeGateway>("stripe");
services.AddKeyedSingleton<IPaymentGateway, PayPalGateway>("paypal");
services.AddKeyedSingleton<IPaymentGateway, SquareGateway>("square");
// Injection via attribute
public sealed class CheckoutService(
[FromKeyedServices("stripe")] IPaymentGateway gateway)
{
public Task ChargeAsync(decimal amount) => gateway.ChargeAsync(amount);
}
// Resolution from provider
public sealed class PaymentRouter(IServiceProvider provider)
{
public IPaymentGateway GetGateway(string name)
=> provider.GetRequiredKeyedService<IPaymentGateway>(name);
}
// Base service
services.AddScoped<IOrderService, OrderService>();
// Decorate with cross-cutting concerns (outermost last)
services.Decorate<IOrderService, CachedOrderService>();
services.Decorate<IOrderService, LoggingOrderService>();
// Decorator implementation
public sealed class CachedOrderService(
IOrderService inner,
HybridCache cache) : IOrderService
{
public async Task<Order?> GetOrderAsync(Guid id, CancellationToken ct)
{
return await cache.GetOrCreateAsync(
$"orders:{id}",
async token => await inner.GetOrderAsync(id, token),
cancellationToken: ct);
}
}
services.AddScoped<IReportGenerator>(sp =>
{
var config = sp.GetRequiredService<IOptions<ReportOptions>>().Value;
return config.Format switch
{
"pdf" => sp.GetRequiredService<PdfReportGenerator>(),
"csv" => sp.GetRequiredService<CsvReportGenerator>(),
_ => throw new InvalidOperationException(
$"Unknown report format: {config.Format}")
};
});
// Background services are singletons — create scopes for scoped deps
public sealed class OrderProcessor(
IServiceScopeFactory scopeFactory,
ILogger<OrderProcessor> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
using var scope = scopeFactory.CreateScope();
var repository = scope.ServiceProvider
.GetRequiredService<IOrderRepository>();
var unitOfWork = scope.ServiceProvider
.GetRequiredService<IUnitOfWork>();
await ProcessPendingOrdersAsync(repository, unitOfWork, ct);
await Task.Delay(TimeSpan.FromSeconds(30), ct);
}
}
}
// Strongly-typed options with validation
services.AddOptions<DatabaseOptions>()
.BindConfiguration(DatabaseOptions.SectionName)
.ValidateDataAnnotations()
.ValidateOnStart();
services.AddOptions<JwtOptions>()
.BindConfiguration("Jwt")
.ValidateDataAnnotations()
.ValidateOnStart();
// BAD: Service locator — hides dependencies
public class OrderService(IServiceProvider provider)
{
public void Process()
{
var repo = provider.GetRequiredService<IOrderRepository>();
}
}
// BAD: Captive dependency — scoped inside singleton
services.AddSingleton<MySingleton>(); // holds IOrderRepository (scoped)
// BAD: Resolving in constructor
public class OrderService(IServiceProvider provider)
{
private readonly IOrderRepository _repo =
provider.GetRequiredService<IOrderRepository>(); // resolves too early
}
// BAD: new-ing up services manually
public class OrderController
{
private readonly OrderService _service = new OrderService(
new OrderRepository(new AppDbContext())); // defeats DI
}
builder.Services.Add calls in Program.csIServiceCollection extension methods in the codebase[FromKeyedServices] usage (indicates .NET 8+ keyed DI)AddScoped, AddTransient, AddSingleton registration patternsIServiceScopeFactory usage in background servicesScrutor package (provides Decorate<> extension)builder.Services.Add* calls into extension methods per layerValidateOnStart() to all Options registrationsScrutor for decorator support if not already available:
<PackageReference Include="Scrutor" />
| Scenario | Lifetime | Notes |
|---|---|---|
| DbContext | Scoped | One per request |
| Repository | Scoped | Matches DbContext lifetime |
| HttpClient handler | Transient | Via IHttpClientFactory |
| Cache wrapper | Singleton | Thread-safe required |
| Options | Singleton | Via IOptions |
| Current user | Scoped | Per-request context |
| Background service | Singleton | Use IServiceScopeFactory inside |