Handlers
Namespace: RA.Utilities.Dispatcher.Handlers
The RA.Utilities.Dispatcher.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 - Result-based returns — return
ValueTask<Result<TResponse>>/ValueTask<Result>; expected failures are returned asResult.Failure(...), never thrown - Context support for free — a protected virtual context-aware
HandleAsyncoverload lets you consumePipelineContext<T>data when you need it
The base classes are pure passthroughs: they take no ILogger, log nothing, and wrap nothing. Expected failures are reported by returning Result.Failure(...) with a typed exception from RA.Utilities.Core.Exceptions. 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.Core.Results;
using RA.Utilities.Dispatcher.Handlers;
public class CreateProductHandler : RequestHandler<CreateProductCommand, int>
{
private readonly IProductRepository _productRepository;
public CreateProductHandler(IProductRepository productRepository)
{
_productRepository = productRepository;
}
public override async ValueTask<Result<int>> HandleAsync(
CreateProductCommand command, CancellationToken cancellationToken)
{
if (await _productRepository.DoesProductExistAsync(command.Name))
return Result.Failure<int>(new ConflictException(nameof(Product), command.Name));
// Business logic here — return the result directly
var product = new Product { Name = command.Name, Price = command.Price };
var id = await _productRepository.AddAsync(product);
return Result.Success(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.
There is no try-catch wrapper — exceptions you throw propagate to the caller; expected failures should be returned as Result.Failure(...).
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.