원클릭으로
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 designing gRPC services, proto files, or adding gRPC-Web or JSON transcoding.
Use when writing async code, propagating CancellationTokens, or fixing async/await pitfalls.
Use when applying or enforcing C# coding style — namespaces, sealed classes, var usage, XML docs.
Use when applying modern C# idioms — records, pattern matching, primary constructors, collection expressions.
Use when selecting or implementing design patterns in C# — factory, builder, strategy, decorator, or mediator.
Use when implementing error handling with domain exceptions, ProblemDetails, or RpcException mapping.
| name | dependency-injection |
| description | Use when registering services, choosing lifetimes, or implementing DI patterns like decorator or keyed services. |
| when_to_use | Use when registering services, choosing lifetimes, or implementing DI patterns like decorator or keyed services. |
| metadata | {"category":"core","agent":"dotnet-architect"} |
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 |