Repository navigation
1.2.0
OpenMediatR 1.2.0
Pre / Post Processors
OpenMediatR now supports IRequestPreProcessor<TRequest> and IRequestPostProcessor<TRequest, TResponse>, matching MediatR's API.
New types (in OpenMediatR.Abstractions)
IRequestPreProcessor<TRequest>—Task Process(TRequest, CancellationToken). Multiple pre-processors per request type are supported and run sequentially in registration order before the handler.IRequestPostProcessor<TRequest, TResponse>—Task Process(TRequest, TResponse, CancellationToken). Run after the handler returns successfully; skipped when the handler or any preceding step throws.
Two registration paths
Both paths trigger automatic wiring of the matching pipeline-behavior wrappers (RequestPreProcessorBehavior<,>, RequestPostProcessorBehavior<,>).
Explicit (recommended for full control):
services.AddOpenMediatR(cfg =>
{
cfg.RegisterServicesFromAssemblyContaining<MyRequestHandler>();
cfg.AddRequestPreProcessor<ValidateUser>();
cfg.AddRequestPostProcessor<AuditUserAccess>();
});AddRequestPreProcessor<T> / AddRequestPostProcessor<T> register T for every closed IRequestPreProcessor<TRequest> (or IRequestPostProcessor<TRequest, TResponse>) it implements — so a single processor class can cover multiple request types.
Opt-in assembly scan (mirrors MediatR's AutoRegisterRequestProcessors):
services.AddOpenMediatR(cfg =>
{
cfg.RegisterServicesFromAssemblyContaining<MyRequestHandler>();
cfg.AutoRegisterRequestProcessors = true;
});When enabled, the assembly scan picks up every processor implementation and wires the wrapper behaviors automatically. Default is false — without the flag, scanning ignores processor types.
Pipeline ordering
User pipeline behaviors registered via AddOpenBehavior wrap the processor wrappers (outer position):
- Outermost user behavior
beforelogic - … inner user behaviors
before - Pre-processors (in order)
- Handler
- Post-processors (in order)
- … inner user behaviors
after - Outermost user behavior
after
Example
public sealed class ValidateUser : IRequestPreProcessor<GetUser>
{
public Task Process(GetUser request, CancellationToken cancellationToken)
{
if (request.Id <= 0) throw new ArgumentException("Invalid user id.");
return Task.CompletedTask;
}
}
public sealed class AuditUserAccess : IRequestPostProcessor<GetUser, User>
{
public Task Process(GetUser request, User response, CancellationToken cancellationToken)
{
// record the access
return Task.CompletedTask;
}
}Behavior change: AddOpenMediatR no longer throws on missing assemblies
AddOpenMediatR previously threw ArgumentException when the configuration registered no assemblies. It now matches MediatR's behavior: completes silently, registers the core services, and lets you compose the rest yourself (explicit handler registrations, AddRequestPreProcessor<T>, etc.). Existing code that did supply assemblies is unaffected.
Fix: idempotent registration on repeated AddOpenMediatR calls
Calling AddOpenMediatR more than once (e.g. when composing modular DI setups) previously registered every scanned INotificationHandler<> implementation, every user pipeline behavior added via AddOpenBehavior / AddOpenStreamBehavior, and every pre/post-processor twice — causing each to execute twice on a single dispatch. Switched to TryAddEnumerable for all of them, which deduplicates by (ServiceType, ImplementationType). Different implementations of the same interface still coexist as expected.
IRequestHandler<,> and IStreamRequestHandler<,> were already idempotent via TryAddTransient and are unchanged.
Compatibility
- Purely additive: existing
Send,Publish,CreateStream, and pipeline-behavior code is unaffected. - Pre/post-processor scanning defaults to off; existing apps see no behavioral change unless they opt in or register processors explicitly.
- Targets
net8.0andnet10.0.