Handlers
Namespace: RA.Utilities.Feature.Handlers
The RA.Utilities.Feature.Handlers namespace provides abstract base classes that implement the IRequestHandler interfaces. These base classes handle the interface plumbing for you, so your handlers only override one HandleAsync method with business logic.
✨ Why Use Base Handlers?
Inheriting from these base classes instead of implementing the interfaces directly gives you:
- No interface plumbing — implement a single abstract
HandleAsyncmethod; the interface members are implemented explicitly - Direct returns — return plain values (
Task<TResponse>/Task); noResultwrapping, no implicit conversions - Context support for free — a protected virtual context-aware
HandleAsyncoverload lets you consumePipelineContext<T>data when you need it
Since v10.2.0 the base classes are pure passthroughs: they no longer take an ILogger, no longer log requests, and no longer catch exceptions. Failures are reported by throwing typed exceptions from RA.Utilities.Core.Exceptions (mapped to HTTP responses by the API layer's GlobalExceptionHandler). For request-level logging, register the LoggingBehavior.
📦 Available Base Classes
| Base Class | Implements | Use Case |
|---|---|---|
RequestHandler<TRequest> | IRequestHandler<TRequest> | Commands with no return value |
RequestHandler<TRequest, TResponse> | IRequestHandler<TRequest, TResponse> | Commands/queries that return data |
🚀 Usage
using RA.Utilities.Core.Exceptions;
using RA.Utilities.Feature.Handlers;
public class CreateProductHandler : RequestHandler<CreateProductCommand, int>
{
private readonly IProductRepository _productRepository;
public CreateProductHandler(IProductRepository productRepository)
{
_productRepository = productRepository;
}
public override async Task<int> HandleAsync(
CreateProductCommand command, CancellationToken cancellationToken)
{
if (await _productRepository.DoesProductExistAsync(command.Name))
throw new ConflictException(nameof(Product), command.Name);
// Business logic here — return the value directly
var product = new Product { Name = command.Name, Price = command.Price };
var id = await _productRepository.AddAsync(product);
return id;
}
}
⚙️ How It Works
The base class implements the IRequestHandler interface explicitly, forwarding both the plain and the context-aware HandleAsync overloads to your overrides:
HandleAsync(request, ct)— the public abstract member you override with business logic.HandleAsync<TContext>(request, context, ct)— a protected virtual member that delegates to the abstract one by default; override it to read or writePipelineContext<T>data.
Exceptions thrown inside your override propagate out of the mediator unchanged — there is no try-catch wrapper.
Handlers are auto-registered: the source generator shipped in the package discovers every concrete IRequestHandler<,> / IRequestHandler<> implementation and AddMediator() applies the registrations (scoped). See the Auto Registration guide for details.
RequestHandler
RequestHandler is an abstract base class that implements the IRequestHandler interface. It implements the interface's plumbing so derived handlers only override a single HandleAsync method with their business logic.