SkillAgentSearch skills...

ModernMediator

Modern mediator for .NET with pub/sub, request/response, weak references, and UI thread dispatching

Install / Use

/learn @EvanscoApps/ModernMediator
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

ModernMediator

NuGet

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
  • StreamingIAsyncEnumerable support for large datasets
  • Pub/Sub (Notifications) — DI-based notification dispatch via IPublisher
  • Pub/Sub with Callbacks — Collect responses from multiple subscribers
  • Result<T> Patternreadonly struct with implicit conversions, Map, and GetValueOrDefault for 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 and AddTimeout()
  • 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 via AddAudit()
  • Built-in IdempotencyBehavior — deduplicates requests marked [Idempotent] by key and TTL using any IIdempotencyStore; registered via AddIdempotency()
  • Built-in CircuitBreakerBehavior — per-request-type circuit breaker via [CircuitBreaker] attribute; open circuit throws CircuitBreakerOpenException; registered via AddCircuitBreaker()
  • Built-in RetryBehavior — automatic retry with configurable count and delay strategy (None, Fixed, Linear, Exponential) via [Retry] attribute; registered via AddRetry()

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 with MapMediatorEndpoints() for Minimal API integration

Performance

  • ValueTask PipelineIValueTaskRequestHandler and ISender.SendAsync for zero-allocation dispatch
  • Closure EliminationRequestHandlerDelegate<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 IntegrationActivitySource and Meter with RequestCounter and RequestDuration via AddTelemetry()

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 (in ModernMediator.AspNetCore) resolves UserId and UserName from IHttpContextAccessor

Async-First Design

  • True Async HandlersSubscribeAsync with proper Task.WhenAll aggregation
  • Cancellation Support — All async operations respect CancellationToken
  • Parallel Execution — Notifications execute handlers concurrently

Error Handling

  • Three PoliciesContinueAndAggregate, 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 Injectionservices.AddModernMediator()
  • Assembly Scanning — Auto-discover handlers, behaviors, and processors
  • Multi-targetnet8.0 and net8.0-windows
  • Interface-firstIMediator for 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 
View on GitHub
GitHub Stars40
CategoryDevelopment
Updated4d ago
Forks7

Languages

C#

Security Score

90/100

Audited on Mar 25, 2026

No findings