ModernMediator
Modern mediator for .NET with pub/sub, request/response, weak references, and UI thread dispatching
Install / Use
/learn @EvanscoApps/ModernMediatorREADME
ModernMediator
A modern, feature-rich mediator library for .NET 8 that combines the best of pub/sub and request/response patterns with advanced features for real-world applications. Zero reflection in hot paths, Native AOT compatible, compile-time diagnostics, and a ValueTask pipeline for zero-allocation dispatch.
Status: Stable
Production-ready. Please report issues on GitHub.
Documentation
📖 Interactive Tutorial 📊 Benchmarks
Do You Need a Mediator?
Some developers argue that the mediator pattern is unnecessary indirection. They're not wrong — if all you're doing is routing a request to a single handler with no cross-cutting concerns, a mediator adds complexity without value. You can inject a service directly, call a method on it, and it works fine.
The pattern earns its keep in two situations.
The first is pipeline behaviors. When every command needs validation, logging, telemetry, timeout enforcement, and eventually caching and retry — and you want those concerns applied consistently without every handler author remembering to wire them up manually — a mediator stops being ceremony and starts being infrastructure. The alternative is decorators or hand-rolled middleware, which either requires manual wiring per handler or introduces the same registration complexity the mediator solves more cleanly.
The second is plugin architectures. When you need to discover and dispatch to handlers that don't exist at compile time in the host application, the mediator pattern isn't a convenience — it's the natural solution. Runtime subscribe/unsubscribe, weak references for handler lifecycle management, and string key routing all support this use case.
If your project doesn't benefit from either of these, don't use a mediator. If it does, ModernMediator is designed to make that choice pay off.
When Do You Need Pipeline Behaviors?
When you have logic that applies across many handlers and you don't want to repeat it inside each one.
Validation is the simplest example. Every command that accepts user input needs validation. Without a pipeline behavior, every handler starts with the same boilerplate — check the input, throw if it's bad, then do the actual work. Multiply that across fifty handlers and you have fifty places to forget, fifty places to get wrong, and fifty places to update when your validation strategy changes. A ValidationBehavior runs before every handler automatically. The handler author writes a FluentValidation validator, registers it, and never thinks about the plumbing.
Logging is the same story. You want to know that a request entered the pipeline, how long it took, and whether it succeeded or failed. Without a behavior, you're scattering logger calls across every handler. With a LoggingBehavior, it's applied uniformly and the handler code stays focused on business logic.
Then it cascades: telemetry — you want ActivitySource traces and duration metrics on every dispatch without instrumenting each handler individually. Timeout enforcement — you want a hard ceiling on handler execution time, applied via a [Timeout] attribute rather than each handler managing its own CancellationTokenSource. Authorization — you want policy checks before the handler even runs, not buried inside it.
The pattern is always the same: a concern that is orthogonal to the handler's purpose, that applies to many or all handlers, and that you want enforced consistently rather than relying on each developer to remember to include it. One behavior class, registered once, applied everywhere.
ModernMediator ships built-in behaviors for validation (via FluentValidation), logging, telemetry, and timeout enforcement. You don't need to write these yourself.
Features
Core Patterns
- Request/Response — Send requests and receive typed responses
- Streaming —
IAsyncEnumerablesupport for large datasets - Pub/Sub (Notifications) — DI-based notification dispatch via
IPublisher - Pub/Sub with Callbacks — Collect responses from multiple subscribers
- Result<T> Pattern —
readonly structwith implicit conversions,Map, andGetValueOrDefaultfor railway-oriented error handling
Pipeline
- Pipeline Behaviors — Wrap handler execution for cross-cutting concerns
- Pre-Processors — Run logic before handlers execute
- Post-Processors — Run logic after handlers complete
- Exception Handlers — Clean, typed exception handling separate from business logic
- Built-in LoggingBehavior — Request/response logging with configurable levels via
AddLogging() - Built-in TimeoutBehavior — Per-request timeout via
[Timeout(ms)]attribute andAddTimeout() - Built-in ValidationBehavior — FluentValidation integration via
ModernMediator.FluentValidation - Built-in AuditBehavior — per-request audit recording (type, user, trace ID, duration, outcome) dispatched to any
IAuditWriter; opt out with[NoAudit]; registered viaAddAudit() - Built-in IdempotencyBehavior — deduplicates requests marked
[Idempotent]by key and TTL using anyIIdempotencyStore; registered viaAddIdempotency() - Built-in CircuitBreakerBehavior — per-request-type circuit breaker via
[CircuitBreaker]attribute; open circuit throwsCircuitBreakerOpenException; registered viaAddCircuitBreaker() - Built-in RetryBehavior — automatic retry with configurable count and delay strategy (None, Fixed, Linear, Exponential) via
[Retry]attribute; registered viaAddRetry()
Source Generators & AOT
- Source Generators — Compile-time code generation eliminates reflection
- Native AOT Compatible — Full support for ahead-of-time compilation
- Compile-Time Diagnostics — 10 diagnostic rules (MM001–MM008, MM100, MM200) catch problems during build
- Zero Reflection — Generated
AddModernMediatorGenerated()for maximum performance - CachingMode — Eager (default) or Lazy initialization for cold start optimization
- ASP.NET Core Endpoint Generation —
[Endpoint]attribute withMapMediatorEndpoints()for Minimal API integration
Performance
- ValueTask Pipeline —
IValueTaskRequestHandlerandISender.SendAsyncfor zero-allocation dispatch - Closure Elimination —
RequestHandlerDelegate<TRequest, TResponse>passes request and token explicitly - Lower allocations than MediatR on every benchmark — see 📊 Benchmarks
- 4x faster cold start than MediatR via source-generated registration
Observability
- OpenTelemetry Integration —
ActivitySourceandMeterwithRequestCounterandRequestDurationviaAddTelemetry()
Interface Segregation
- ISender — Request/response dispatch
- IPublisher — Notification publishing
- IStreamer — Streaming dispatch
- IMediator — Composes all three; all registered in DI as forwarding aliases
Advanced Capabilities
- Weak References — Handlers can be garbage collected, preventing memory leaks
- Strong References — Opt-in for handlers that must persist
- Runtime Subscribe/Unsubscribe — Dynamic handler registration (perfect for plugins)
- Predicate Filters — Filter messages at subscription time
- Covariance — Subscribe to base types, receive derived messages
- String Key Routing — Topic-based subscriptions alongside type-based
- ICurrentUserAccessor — abstraction for resolving current user identity in pipeline behaviors;
HttpContextCurrentUserAccessor(inModernMediator.AspNetCore) resolvesUserIdandUserNamefromIHttpContextAccessor
Async-First Design
- True Async Handlers —
SubscribeAsyncwith properTask.WhenAllaggregation - Cancellation Support — All async operations respect
CancellationToken - Parallel Execution — Notifications execute handlers concurrently
Error Handling
- Three Policies —
ContinueAndAggregate,StopOnFirstError,LogAndContinue - HandlerError Event — Hook for logging and monitoring
- Exception Unwrapping — Clean stack traces without reflection noise
UI Thread Support
- Built-in Dispatchers — WPF, WinForms, MAUI, ASP.NET Core, Avalonia
- SubscribeOnMainThread — Automatic UI thread marshalling
Modern .NET Integration
- Dependency Injection —
services.AddModernMediator() - Assembly Scanning — Auto-discover handlers, behaviors, and processors
- Multi-target —
net8.0andnet8.0-windows - Interface-first —
IMediatorfor testability and mocking
Installation
dotnet add package ModernMediator
Optional packages:
dotnet add package ModernMediator.FluentValidation
dotnet add package ModernMediator.AspNetCore
dotnet add package ModernMediator.Audit.Serilog
dotnet add package ModernMediator.Audit.EntityFramework
dotnet add package ModernMediator.Idempotency.EntityFramework
Quick Start
Setup with Dependency Injection (Recommended)
IMediator is registered as Scoped by default, allowing handlers to resolve scoped dependencies like DbContext.
// Program.cs - with assembly scanning (uses reflection)
services.AddModernMediator(config =>
{
config.RegisterServicesFromAssemblyContaining<Program>();
});
// Or use source-generated registration (AOT-compatible, no reflection)
services.AddModernMediatorGenerated();
// Or with configuration
services.AddModernMediator(config =>
{
config.RegisterServicesFromAssemblyContaining<Program>();
config.ErrorPolicy = ErrorPolicy.LogAndContinue;
config.Configure(m => m.SetDispatcher(new WpfDispatcher()));
});
Setup without DI
// Singleton (shared instance) - ideal
