Mediator

repository·main·Indexed 25 days ago

https://github.com/martinothamar/mediator

A high-performance .NET implementation of the Mediator pattern utilizing source generators for build-time safety and Native AOT compatibility. It provides a reflection-free alternative to MediatR, supporting Requests, Commands, Queries, and Notifications with optimized cold start times and low memory allocation. The library is distributed via Mediator.SourceGenerator and Mediator.Abstractions NuGet packages.

Tokens
8.9K
Snippets
23
Records
51
Agent score
87%

What's inside Mediator

  1. Overview of Mediator performance and features

    main

    Mediator is a high-performance .NET implementation of the Mediator pattern that uses source generators instead of reflection.

    Key features include:

    • Native AOT Support: Full support without reflection or runtime code generation, making it ideal for serverless, edge, and mobile environments.
    • Build-time Safety: The source generator emits diagnostics (warnings/errors) during development if handlers are missing or configuration is incorrect, preventing runtime errors.
    • High Performance: Uses monomorphized Send methods and fast dictionary lookups. For maximum performance, use the concrete Mediator class instead of the IMediator interface.
    • Low Cold Start: Optimized for fast startup times.
  2. Compare Mediator performance with MediatR

    main

    The benchmarks include a comparison between this library and MediatR across two specific scenarios:

    1. Initialization: Resolving IMediator from IServiceProvider.
    2. Cold start: Resolving IMediator from IServiceProvider and sending a single request using the IRequest<> overload.

    Note: These comparisons do not include a comparison to a direct handler call (which is treated as a baseline in other benchmarks).

  3. Run the ASPNET Core Indirect sample application

    main

    The ASPNET_Core_Indirect sample demonstrates how to use Mediator.Abstraction when it is referenced indirectly through other projects. This scenario ensures the Source Generator correctly identifies types even when they are not directly referenced in the entry point project.

    To run the application:

    1. Use Visual Studio or the dotnet CLI.
    2. Once running, the Swagger UI is available at http://localhost:5000/swagger/index.html.
    3. To test the API, you can use the get-weather-forecast.http file with the VSCode REST Client extension.
  4. Add Mediator to DI container

    main

    In your ConfigureServices or equivalent method, call AddMediator(). By default, it uses the Mediator namespace. This method automatically registers your handlers using the source generator.

    If you need to specify which assemblies to scan for handlers, use the MediatorOptions configuration overload.

    using Mediator;
    using Microsoft.Extensions.DependencyInjection;
    using System;
    
    var services = new ServiceCollection();
    
    // Default registration
    services.AddMediator();
    
    // Registration with specific assemblies
    services.AddMediator((MediatorOptions options) => options.Assemblies = [typeof(Ping)]);
    
    using var serviceProvider = services.BuildServiceProvider();
    var mediator = serviceProvider.GetRequiredService<IMediator>();
  5. Install Mediator packages

    main

    To use Mediator, you need two NuGet packages:

    1. Mediator.SourceGenerator: Install this in your edge/outermost project (e.g., ASP.NET Core application or Background worker) to generate the IMediator implementation and dependency injection setup.
    2. Mediator.Abstractions: Use this in any project where you define message types and handlers.

    Standard message handlers are automatically picked up and added to the DI container via the generated AddMediator method. However, pipeline behaviors must be added manually.

  6. Explore Mediator usage samples

    main

    The samples/ directory provides various implementation patterns for Mediator:

    • Basic usage: Located in samples/basic/, demonstrating core primitives like requests, notifications, and IPipelineBehavior.
    • Application architectures: Located in samples/apps/, showing full-featured usage in APIs and applications (e.g., Clean Architecture).
    • Specific use cases: Located in samples/use-cases/, covering patterns like Autofac integration and query caching.
    • Showcase: The samples/Showcase/ directory contains the implementation for the features demonstrated in the project's root README.
  7. Enable Mediator Telemetry

    main

    Mediator emits telemetry for requests, streams, and notifications using System.Diagnostics.Metrics and System.Diagnostics.ActivitySource.

    To use it with OpenTelemetry, configure the Telemetry property in AddMediator and wire up the Meter and ActivitySource in your application's OpenTelemetry setup.

    Note: If you used a custom namespace in your configuration, use that generated concrete mediator type instead of Mediator.Mediator when adding the meter/source to OpenTelemetry.

    services.AddMediator((MediatorOptions options) =>
    {
        options.Telemetry.EnableMetrics = true;
        options.Telemetry.EnableTracing = true;
        options.Telemetry.MeterName = "<optional-custom-name>";
        options.Telemetry.ActivitySourceName = "<optional-custom-name>";
    });
    
    // In your OpenTelemetry setup:
    builder.Services
        .AddOpenTelemetry()
        .WithMetrics(metrics => metrics.AddMeter(Mediator.Mediator.MeterName))
        .WithTracing(tracing => tracing.AddSource(Mediator.Mediator.ActivitySourceName));
  8. Understand Message Validation via MessageValidatorBehaviour

    main

    In this sample, validation is implemented using a pipeline behavior called MessageValidatorBehaviour. This behavior automatically processes any message sent through Mediator that implements the IValidate interface.

    Note: This specific implementation uses a ValidationException for control flow to 'return early' from the pipeline. While functional for the sample, for production systems, it is recommended to use types like Result<TResponse, ValidationError> or OneOf<TResponse, ValidationError> to handle success and failure cases without relying on exceptions.