DI registration patterns, service lifetimes, keyed services, Options pattern, decorator pattern, and factory delegates. Trigger: DI, dependency injection, services, registration, lifetime, singleton, scoped.
From dotnet-ai-kitnpx claudepluginhub faysilalshareef/dotnet-ai-kit --plugin dotnet-ai-kitThis skill uses the workspace's default tool permissions.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
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<T> |
| Current user | Scoped | Per-request context |
| Background service | Singleton | Use IServiceScopeFactory inside |