A high-performance, feature-rich implementation of the Mediator pattern for .NET applications. Built for modern development with comprehensive CQRS support, advanced middleware pipelines, real-time streaming, event-driven architecture, and full observability. Features include OpenTelemetry integration with distributed tracing and metrics collection, extensive debug logging infrastructure, advanced statistics tracking, fluent configuration API with environment-aware settings, type-constrained middleware for selective execution, comprehensive notification system with hybrid patterns, native streaming capabilities with IAsyncEnumerable support, and automatic handler discovery. Perfect for cloud-native applications requiring high performance, comprehensive observability, and production-ready architecture patterns.
$ dotnet add package Blazing.MediatorA high-performance, 100% AOT compatible, feature-rich implementation of the Mediator pattern for .NET applications. Built for modern development with comprehensive CQRS support, advanced middleware pipelines, real-time streaming, event-driven architecture, and full observability.
[!CAUTION] Version 3.0.0 has breaking changes. See the Breaking Changes document for a complete list of API changes with before/after comparisons, and the MIGRATION_GUIDE.md for full upgrade steps.
IAsyncEnumerable<T> for real-time processingINotificationHandler<T> discovery and manual INotificationSubscriber<T> observer patternsINotificationPublisher for fully custom strategiesValueTask throughout, zero-allocation dispatch on hot paths, and efficient resource usage[ExcludeFromAutoDiscovery]: Fine-grained opt-out — suppress source generator discovery for individual handlers or middleware without removing themBLAZMED series) for open generic handlers, missing registrations, constraint violations, and AOT incompatibilitiesv3.0.0 replaces all runtime reflection with compile-time source generation, delivering dramatic performance improvements.
Environment: BenchmarkDotNet v0.15.6 · .NET 10.0.3 · AMD Ryzen 7 3700X · 64 GB RAM
| Library | Mean | vs MediatR | Allocated |
|---|---|---|---|
| MediatR 12.5 (baseline) | 72.13 ns | — | 128 B |
| Blazing.Mediator v3.0.0 (source-generated) | 17.49 ns | 75.8% faster | 0 B |
| Blazing.Mediator v2.0.1 (reflection) | 2,002.81 ns | 2,488% slower | 2,160 B |
v3.0.0 vs v2.0.1: 114× faster · zero allocation per call
| Library | Mean | Ratio to MediatR | vs MediatR | Allocated |
|---|---|---|---|---|
| MediatR 12.5 (baseline) | 304.92 ns | 1.00× | — | 488 B |
| Blazing.Mediator v3.0.0 (source-generated) | 90.29 ns | 0.29× | 70.9% faster | 96 B |
| Blazing.Mediator v2.0.1 (reflection) | 3,025.45 ns | 9.74× | 872% slower | 2,768 B |
v3.0.0 vs v2.0.1: 34× faster · 97.0% reduction · −2,672 B per call
| Library | Mean | vs MediatR | Allocated |
|---|---|---|---|
| MediatR 12.5 (baseline) | 112.98 ns | — | 288 B |
| Blazing.Mediator v3.0.0 (source-generated) | 30.68 ns | 72.8% faster | 0 B |
| Blazing.Mediator v2.0.1 (reflection) | 2,184.08 ns | 1,846% slower | 2,136 B |
v3.0.0 vs v2.0.1: 71× faster · zero allocation per call
For full benchmark results including streaming and comparison against martinothamar/Mediator, see the Benchmark Comparison.
Blazing.Mediator supports both .NET 9 and .NET 10, providing a modern, high-performance mediator implementation for current and next-generation .NET applications. The library leverages the latest language features and runtime optimisations to deliver exceptional performance and developer experience across both target frameworks.
dotnet add package Blazing.Mediator
dotnet add package Blazing.Mediator.SourceGenerators
Install-Package Blazing.Mediator
Install-Package Blazing.Mediator.SourceGenerators
.csproj file<PackageReference Include="Blazing.Mediator" Version="3.0.*" />
<PackageReference Include="Blazing.Mediator.SourceGenerators" Version="3.0.*"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />
</PackageReference>
Both packages are required. Blazing.Mediator.SourceGenerators replaces all runtime reflection with compile-time source generation for zero-allocation dispatch.
using Blazing.Mediator;
var builder = WebApplication.CreateBuilder(args);
// Source generator auto-discovers all handlers and middleware at compile time
builder.Services.AddMediator();
// Or with optional runtime configuration (e.g., statistics, telemetry)
builder.Services.AddMediator(MediatorConfiguration.Production());
var app = builder.Build();
app.Run();
// Query (Read operation) - Generic approach
public class GetUserQuery : IRequest<UserDto>
{
public int UserId { get; init; }
}
// Alternative: CQRS naming (same functionality)
public class GetUserQuery : IQuery<UserDto>
{
public int UserId { get; init; }
}
public class GetUserHandler : IRequestHandler<GetUserQuery, UserDto>
// Alternative: public class GetUserHandler : IQueryHandler<GetUserQuery, UserDto>
{
public async Task<UserDto> Handle(GetUserQuery request, CancellationToken cancellationToken)
{
// Your logic here
return new UserDto { Id = request.UserId, Name = "John Doe" };
}
}
// Command (Write operation) - Generic approach
public class CreateUserCommand : IRequest<int>
{
public string Name { get; init; } = string.Empty;
public string Email { get; init; } = string.Empty;
}
public class CreateUserHandler : IRequestHandler<CreateUserCommand, int>
{
public async Task<int> Handle(CreateUserCommand request, CancellationToken cancellationToken)
{
// Your logic here
return 1; // Return new user ID
}
}
Blazing.Mediator provides both generic and CQRS-specific interfaces for maximum flexibility:
| CQRS Interface | Generic Equivalent | Use Case |
|---|---|---|
IQuery<TResponse> | IRequest<TResponse> | Read operations that return data |
ICommand | IRequest | Write operations with no return value |
ICommand<TResponse> | IRequest<TResponse> | Write operations that return data |
IQueryHandler<T,R> | IRequestHandler<T,R> | Handles queries |
ICommandHandler<T> | IRequestHandler<T> | Handles void commands |
ICommandHandler<T,R> | IRequestHandler<T,R> | Handles commands with return values |
Both approaches work identically - choose based on your team's preferences:
IRequest for flexibility and simplicityIQuery/ICommand for explicit business intent[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly IMediator _mediator;
public UsersController(IMediator mediator) => _mediator = mediator;
[HttpGet("{id}")]
public async Task<UserDto> GetUser(int id)
{
return await _mediator.Send(new GetUserQuery { UserId = id });
}
[HttpPost]
public async Task<int> CreateUser(CreateUserCommand command)
{
return await _mediator.Send(command);
}
}
Blazing.Mediator provides a powerful, fluent configuration API that automatically adapts to your environment. Configure your mediator with environment-aware presets, JSON configuration support, and comprehensive validation. The configuration system includes intelligent discovery options for middleware and handlers, advanced statistics tracking, and seamless OpenTelemetry integration. Whether you're running in development, staging, or production, the configuration system ensures optimal settings for your deployment environment.
builder.Services.AddMediator(config =>
{
config.WithStatisticsTracking()
.WithOpenTelemetryIntegration();
// Handlers and middleware are auto-discovered by the source generator at compile time
});
For comprehensive configuration patterns and advanced scenarios, see the Configuration Guide.
For applications with multiple assemblies and complex domain structures, Blazing.Mediator.SourceGenerators automatically discovers all handlers and middleware across assembly boundaries at compile time — no runtime assembly scanning or manual middleware registration required.
// All assemblies are discovered by the source generator at compile time.
// Only runtime options need to be configured.
services.AddMediator(config =>
{
// Enable comprehensive statistics tracking across all assemblies
config.WithStatisticsTracking();
});
The source generator scans every referenced assembly for IRequestHandler<T>, INotificationHandler<T>, IStreamRequestHandler<T,R>, and all middleware types at compile time and emits a fully-typed dispatcher — no AddMiddleware, AddAssemblies, or WithMiddlewareDiscovery calls needed.
See the AnalyzerExample for a complete demonstration of multi-assembly configuration with 92 components across 5 projects, including intentionally missing handlers to showcase the powerful debugging capabilities of the analyser tools.
Native streaming support enables memory-efficient processing of large datasets through IAsyncEnumerable<T>. Perfect for real-time data feeds, large file processing, and server-sent events. The streaming infrastructure includes full middleware pipeline support, allowing you to apply cross-cutting concerns such as logging, validation, and metrics to your streaming operations. This eliminates the need to load entire datasets into memory, providing superior performance for data-intensive applications.
// Streaming request
public class GetDataStream : IStreamRequest<DataItem>
{
public string Source { get; init; } = string.Empty;
}
// Streaming handler
public class GetDataStreamHandler : IStreamRequestHandler<GetDataStream, DataItem>
{
public async IAsyncEnumerable<DataItem> Handle(GetDataStream request, [EnumeratorCancellation] CancellationToken cancellationToken)
{
await foreach (var item in ProcessDataAsync(request.Source, cancellationToken))
{
yield return item;
}
}
}
// Use in controller
[HttpGet("stream")]
public async IAsyncEnumerable<DataItem> StreamData([EnumeratorCancellation] CancellationToken cancellationToken)
{
await foreach (var item in _mediator.SendStream(new GetDataStream { Source = "database" }, cancellationToken))
{
yield return item;
}
}
For detailed streaming patterns and real-world examples, see the Streaming Guide.
Build robust event-driven systems with comprehensive notification support featuring both automatic handler discovery and manual subscription patterns. The notification system supports multiple handlers per event, complete middleware pipelines, and type-constrained processing for optimal performance. Whether you need decoupled domain events, observer patterns, or pub/sub messaging, the notification infrastructure provides the flexibility to implement complex event-driven architectures with ease.
// Domain event
public class UserCreatedNotification : INotification
{
public int UserId { get; init; }
public string Email { get; init; } = string.Empty;
}
// Multiple handlers for the same event
public class EmailWelcomeHandler : INotificationHandler<UserCreatedNotification>
{
public async Task Handle(UserCreatedNotification notification, CancellationToken cancellationToken)
{
// Send welcome email
}
}
public class AnalyticsHandler : INotificationHandler<UserCreatedNotification>
{
public async Task Handle(UserCreatedNotification notification, CancellationToken cancellationToken)
{
// Track user registration
}
}
// Publish event
await _mediator.Publish(new UserCreatedNotification { UserId = userId, Email = email });
For complete event-driven patterns and notification strategies, see the Notification System Guide.
Comprehensive observability is built into every aspect of Blazing.Mediator, providing deep insights into your application's behaviour. Full OpenTelemetry integration delivers distributed tracing, metrics collection, and performance monitoring for cloud-native applications. The statistics system tracks execution patterns, success rates, and performance metrics across all operations. Combined with an extensive debug logging infrastructure, you get complete visibility into request handling, middleware execution, and notification processing for both development debugging and production monitoring.
// Enable OpenTelemetry integration
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing.AddBlazingMediatorInstrumentation())
.WithMetrics(metrics => metrics.AddBlazingMediatorInstrumentation());
// Access built-in statistics
public class HealthController : ControllerBase
{
private readonly IStatisticsService _statistics;
[HttpGet("stats")]
public async Task<MetricsSummary> GetStats()
{
return await _statistics.GetSummaryAsync();
}
}
For complete observability implementation and cloud-native monitoring, see the OpenTelemetry Integration Guide, Statistics Guide, and Logging Guide.
We welcome contributions! Please see our Contributing Guide for details.
dotnet restoredotnet testThis project is licensed under the MIT License - see the LICENSE file for details.
If you like this project or are using it to learn or start your solution, please give it a star. Thanks!
Also, if you find this library useful, and you're feeling really generous, then please consider buying me a coffee ☕.
Comprehensive guides and documentation are available to help you master Blazing.Mediator:
IAsyncEnumerable<T> for real-time data processingBlazing.Mediator naturally implements the Command Query Responsibility Segregation (CQRS) pattern with comprehensive interface support:
Generic Interfaces (Foundation)
IRequest - Commands that don't return data (void operations)IRequest<TResponse> - Queries/Commands that return dataIRequestHandler<TRequest> - Handlers for void operationsIRequestHandler<TRequest, TResponse> - Handlers for operations returning dataCQRS Semantic Interfaces
ICommand - Write operations with no return value (extends IRequest)ICommand<TResponse> - Write operations returning data (extends IRequest<TResponse>)IQuery<TResponse> - Read operations returning data (extends IRequest<TResponse>)ICommandHandler<TRequest> - Command handlers (extends IRequestHandler<TRequest>)ICommandHandler<TRequest, TResponse> - Command handlers with return values (extends IRequestHandler<TRequest, TResponse>)IQueryHandler<TRequest, TResponse> - Query handlers (extends IRequestHandler<TRequest, TResponse>)IStreamRequest<TResponse> - Streaming requests returning IAsyncEnumerable<TResponse>IStreamRequestHandler<TRequest, TResponse> - Handlers for streaming operationsIStreamRequestMiddleware<TRequest, TResponse> - Middleware for streaming pipelinesAutomatic Handler Pattern (Publish-Subscribe)
INotification - Marker interface for domain eventsINotificationHandler<TNotification> - Automatic handlers discovered via DIINotificationMiddleware - Middleware for notification pipelinesManual Subscriber Pattern (Observer)
INotificationSubscriber<TNotification> - Manual subscribers requiring explicit registrationmediator.Subscribe() and mediator.Unsubscribe()ICommand, IQuery<T> or other interfacesBoth approaches work identically - choose based on your team's preferences:
IRequest/IRequestHandler for flexibility and simplicityIQuery/ICommand/IQueryHandler/ICommandHandler for explicit business intentThe library includes comprehensive sample projects demonstrating different approaches:
Blazing.Mediator.Examples - Complete feature showcase and migration guide from MediatR
IAsyncEnumerable<T> for real-time data processingMiddlewareExample - Console application demonstrating comprehensive middleware pipeline and inspection capabilities
IMiddlewarePipelineInspector for debugging and monitoring middleware executionMiddlewarePipelineAnalyzer helper class for runtime pipeline analysis and introspectionTypedMiddlewareExample (NEW!) - Console application demonstrating type-constrained middleware with CQRS interface distinction
ICommand and IQuery interfaces with type-specific middlewareNotificationSubscriberExample - Console application demonstrating manual notification subscription pattern required for client applications
INotificationSubscriber<T>TypedNotificationSubscriberExample (NEW!) - Console application demonstrating type-constrained notification middleware with manual subscription pattern
IOrderNotification, ICustomerNotification, IInventoryNotification)INotificationSubscriber<T> pattern requiring explicit subscriptionINotificationMiddlewarePipelineInspectorNotificationHandlerExample (NEW!) - Console application demonstrating automatic notification handler discovery pattern
INotificationHandler<T>NotificationHybridExample (NEW!) - Console application demonstrating hybrid notification pattern combining automatic and manual approaches
INotificationHandler<T> (automatic) with INotificationSubscriber<T> (manual)TypedNotificationHandlerExample (NEW!) - Console application demonstrating type-constrained notification middleware with automatic handler discovery
INotificationMiddlewarePipelineInspectorTypedNotificationHybridExample (NEW!) - Console application demonstrating the ultimate typed hybrid notification pattern
ECommerce.Api - Demonstrates traditional Controller-based API with conditional middleware and notification system
UserManagement.Api - Demonstrates modern Minimal API approach with standard middleware
Streaming.Api - Demonstrates real-time data streaming with multiple implementation patterns
IAsyncEnumerable<T> streaming with large datasetsOpenTelemetryExample (NEW!) - Comprehensive OpenTelemetry integration demonstration with modern cloud-native architecture
ConfigurationExample (NEW!) - Comprehensive demonstration of Configuration Features with environment-aware settings
AnalyzerExample (NEW!) - Comprehensive multi-assembly analyser demonstration showcasing debugging capabilities across complex domain architectures
MiddlewareAnalysisExtensions and QueryCommandAnalysisExtensions for comprehensive type normalisation and clean output formattingAll of the Example Console applications demonstrate comprehensive MediatorStatistics analysis and middleware analysers, with detailed performance statistics, execution tracking, and pipeline inspection capabilities. These examples showcase real-time monitoring of queries, commands, and notifications with success rates, timing metrics, and handler discovery analysis. For complete details on implementing statistics tracking and performance monitoring in your applications, see the Statistics Guide.
Blazing.Mediator.SourceGenerators — zero-allocation dispatch on request, notification, and streaming hot pathsValueTask Throughout: All handler and middleware return types changed from Task/Task<T> to ValueTask/ValueTask<T> for reduced allocations and improved throughputconfig.AddMiddleware(...) calls neededservices.AddMediator() with no assembly arguments; source generator handles handler and middleware discovery automatically#if USE_SOURCE_GENERATORS Removed: No preprocessor guards required — source generator activates automatically when Blazing.Mediator.SourceGenerators is referencedIMediator lifetime changed from Scoped to Singleton, eliminating per-request DI resolution overheadINotificationPublisher interface allows opt-in concurrent notification dispatch (config.WithConcurrentNotificationPublisher())[ExcludeFromAutoDiscovery] Attribute: Opt individual handlers out of source-generator discoveryMediatorDispatcherBase: New abstract base class bridges the pre-compiled library and the source-generated ContainerMetadataErrorHandlingMiddleware<TRequest, TResponse>) appeared multiple times in telemetry traces due to open generic type definitions being instantiated as multiple closed generic types during executionbuilder.Services.AddMediator(config => { ... }) for improved type safety, enhanced functionality, and streamlined developer experience with IntelliSense supportAddMediator() and AddMediatorFromLoadedAssemblies() methods with boolean parameters as obsolete while maintaining backward compatibility during the transition period with comprehensive migration guidanceICommand, IQuery<T>, IOrderNotification)