Dependency Injection - PogovorovDaniil/Requestum GitHub Wiki
Requestum integrates seamlessly with Microsoft.Extensions.DependencyInjection. This guide covers all aspects of service registration and dependency injection patterns.
The simplest way to register Requestum:
using Microsoft.Extensions.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRequestum(cfg =>
{
// Scans assembly for both handlers and middlewares
cfg.Default(typeof(Program).Assembly);
});This is equivalent to:
builder.Services.AddRequestum(cfg =>
{
cfg.RegisterHandlers(typeof(Program).Assembly);
cfg.RegisterMiddlewares(typeof(Program).Assembly);
});Register only handlers (no middleware):
builder.Services.AddRequestum(cfg =>
{
cfg.RegisterHandlers(typeof(Program).Assembly);
});Register only middlewares (handlers registered manually):
builder.Services.AddRequestum(cfg =>
{
cfg.RegisterMiddlewares(typeof(Program).Assembly);
});builder.Services.AddRequestum(cfg =>
{
cfg.RegisterHandlers(typeof(Program).Assembly);
});builder.Services.AddRequestum(cfg =>
{
cfg.RegisterHandlers(
typeof(Program).Assembly, // Current assembly
typeof(UserModule).Assembly, // User module
typeof(OrderModule).Assembly, // Order module
typeof(PaymentModule).Assembly // Payment module
);
});builder.Services.AddRequestum(cfg =>
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => a.FullName?.StartsWith("YourCompany") == true)
.ToArray();
cfg.RegisterHandlers(assemblies);
});Set default lifetime for all handlers and middlewares:
builder.Services.AddRequestum(cfg =>
{
// All handlers and middlewares will be Scoped by default
cfg.Lifetime = ServiceLifetime.Scoped;
cfg.RegisterHandlers(typeof(Program).Assembly);
});Available Lifetimes:
-
ServiceLifetime.Transient- New instance per request (default) -
ServiceLifetime.Scoped- One instance per scope (request) -
ServiceLifetime.Singleton- One instance for application lifetime
Override lifetime for specific handlers:
builder.Services.AddRequestum(cfg =>
{
// Global default: Transient
cfg.Lifetime = ServiceLifetime.Scoped;
// Scan assemblies
cfg.RegisterHandlers(typeof(Program).Assembly);
// Override specific handlers
cfg.RegisterHandler<CachedUserQueryHandler>(ServiceLifetime.Singleton);
cfg.RegisterHandler<TransactionalOrderHandler>(ServiceLifetime.Transient);
});builder.Services.AddRequestum(cfg =>
{
cfg.RegisterMiddlewares(typeof(Program).Assembly);
// Override specific middleware
cfg.RegisterMiddleware(typeof(CachingMiddleware<,>), ServiceLifetime.Singleton);
cfg.RegisterMiddleware(typeof(TransactionMiddleware<,>), ServiceLifetime.Scoped);
});builder.Services.AddRequestum(cfg =>
{
// Register specific handlers without assembly scanning
cfg.RegisterHandler<CreateUserCommandHandler>();
cfg.RegisterHandler<GetUserQueryHandler>();
cfg.RegisterHandler<SendWelcomeEmailReceiver>();
});builder.Services.AddRequestum(cfg =>
{
cfg.RegisterMiddleware(typeof(LoggingMiddleware<,>));
cfg.RegisterMiddleware(typeof(ValidationMiddleware<,>));
cfg.RegisterMiddleware(typeof(TransactionMiddleware<,>));
});builder.Services.AddRequestum(cfg =>
{
// Scan for most handlers
cfg.RegisterHandlers(typeof(Program).Assembly);
// Manually register from other assemblies
cfg.RegisterHandler<ExternalApiHandler>(ServiceLifetime.Singleton);
cfg.RegisterHandler<LegacySystemHandler>(ServiceLifetime.Scoped);
// Manually register middleware
cfg.RegisterMiddleware(typeof(CustomAuthMiddleware<,>), ServiceLifetime.Scoped);
});By default, events must have at least one receiver:
builder.Services.AddRequestum(cfg =>
{
cfg.RequireEventHandlers = true; // Default
cfg.RegisterHandlers(typeof(Program).Assembly);
});
// Publishing event without receivers throws exception
await requestum.PublishAsync(new UserCreatedEvent(...)); // ❌ Exception if no receiversAllow publishing events without receivers:
builder.Services.AddRequestum(cfg =>
{
cfg.RequireEventHandlers = false; // Allow events without receivers
cfg.RegisterHandlers(typeof(Program).Assembly);
});
// Publishing event without receivers is OK (no-op)
await requestum.PublishAsync(new UserCreatedEvent(...)); // ✅ No errorHandlers receive dependencies via constructor:
public class CreateUserCommandHandler : IAsyncCommandHandler<CreateUserCommand>
{
private readonly IUserRepository _repository;
private readonly ILogger<CreateUserCommandHandler> _logger;
private readonly IEmailService _emailService;
private readonly IRequestum _requestum;
public CreateUserCommandHandler(
IUserRepository repository,
ILogger<CreateUserCommandHandler> logger,
IEmailService emailService,
IRequestum requestum)
{
_repository = repository;
_logger = logger;
_emailService = emailService;
_requestum = requestum;
}
public async Task ExecuteAsync(CreateUserCommand command, CancellationToken ct = default)
{
// Use injected dependencies
_logger.LogInformation("Creating user: {Name}", command.Name);
var user = new User(command.Name, command.Email);
await _repository.AddAsync(user, ct);
await _requestum.PublishAsync(new UserCreatedEvent(user.Id, user.Name, user.Email), ct);
}
}var builder = WebApplication.CreateBuilder(args);
// Register your dependencies
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<IEmailService, EmailService>();
builder.Services.AddSingleton<ICacheService, CacheService>();
builder.Services.AddDbContext<AppDbContext>();
// Register Requestum (will use the registered dependencies)
builder.Services.AddRequestum(cfg =>
{
cfg.Default(typeof(Program).Assembly);
});public static class RequestumDiagnostics
{
public static void PrintRegisteredHandlers(IServiceProvider services)
{
var descriptors = services.GetService<IServiceCollection>();
var handlers = descriptors?
.Where(d => typeof(IBaseHandler).IsAssignableFrom(d.ServiceType))
.ToList();
Console.WriteLine("Registered Handlers:");
foreach (var handler in handlers ?? [])
{
Console.WriteLine($"- {handler.ServiceType.Name} ({handler.Lifetime})");
}
}
}// ✅ Good - Transient for stateless handlers
cfg.RegisterHandler<CreateUserCommandHandler>(ServiceLifetime.Transient);
// ✅ Good - Scoped for handlers using DbContext
cfg.RegisterHandler<OrderCommandHandler>(ServiceLifetime.Scoped);
// ✅ Good - Singleton for stateless, cacheable handlers
cfg.RegisterHandler<GetConfigurationQueryHandler>(ServiceLifetime.Singleton);
// ❌ Bad - Singleton with state
public class StatefulHandler : ICommandHandler<MyCommand>
{
private int _counter; // State! Don't use Singleton
}// ❌ Bad - Service Locator
public class MyHandler : IAsyncCommandHandler<MyCommand>
{
private readonly IServiceProvider _serviceProvider;
public async Task ExecuteAsync(MyCommand command, CancellationToken ct = default)
{
// Don't do this!
var repository = _serviceProvider.GetRequiredService<IRepository>();
}
}
// ✅ Good - Constructor Injection
public class MyHandler : IAsyncCommandHandler<MyCommand>
{
private readonly IRepository _repository;
public MyHandler(IRepository repository)
{
_repository = repository;
}
}// ✅ Good - Organized by feature
public static class DependencyInjection
{
public static IServiceCollection AddApplication(this IServiceCollection services)
{
services.AddRequestum(cfg =>
{
cfg.Default(typeof(DependencyInjection).Assembly);
});
return services;
}
public static IServiceCollection AddInfrastructure(this IServiceCollection services)
{
services.AddScoped<IUserRepository, UserRepository>();
services.AddScoped<IEmailService, EmailService>();
return services;
}
}
// Usage
builder.Services
.AddApplication()
.AddInfrastructure();