MEDIATION

MEDIATION

The crazy fast, always free MediatR alternative.

Read Me

Meditation is a lightweight, high-performance alternative to MediatR that uses Source Generation to achieve zero-reflection dispatch. By moving the logic of finding and calling handlers from runtime to compile-time, Meditation eliminates the overhead typically associated with mediator libraries, making it "super fast" and AOT-friendly.

Why Meditation?

  • 🚀 Zero Reflection: No runtime assembly scanning or MethodInfo.Invoke. Dispatching is done via source-generated pattern matching.
  • ⚡ Super Fast: Benchmarks (hypothetically) show near-direct method call performance.
  • 🛠️ AOT Compatible: Perfect for .NET Native AOT applications because it doesn't rely on dynamic code execution.
  • 🛡️ Type Safe: Compile-time validation of your requests and handlers.
  • 🔌 Interceptors: Built-in support for request/response interception (similar to behaviors but simpler).

Getting Started

1. Define a Request

Requests implement IRequest (for no response) or IRequest<TResponse>.

using Meditation;

public class PingQuery : IRequest<string>
{
    public string Message { get; set; }
}

2. Define a Handler

Handlers implement IHandler<TRequest, TResponse> or IHandler<TRequest>.

using Meditation;

public class PingHandler : IHandler<PingQuery, string>
{
    public Task<string> HandleAsync(PingQuery request, CancellationToken ct)
    {
        return Task.FromResult($"Pong: {request.Message}");
    }
}

3. Register Services

Since Meditation uses source generation, the Dispatcher is generated specifically for your handlers. You just need to register your handlers and the generated Dispatcher.

// In your Program.cs or Startup.cs
builder.Services.AddScoped<PingHandler>();
builder.Services.AddScoped<IDispatcher, Dispatcher>();

4. Dispatch a Request

Inject IDispatcher and send your request.

app.MapGet("/ping", async (IDispatcher dispatcher) =>
{
    var response = await dispatcher.Dispatch(new PingQuery { Message = "Hello" });
    return Results.Ok(response);
});

Interceptors

You can add cross-cutting concerns by implementing IInterceptor.

public class LoggingInterceptor : IInterceptor
{
    public Task OnRequestAsync(object request)
    {
        Console.WriteLine($"Handling {request.GetType().Name}");
        return Task.CompletedTask;
    }

    public Task OnResponseAsync(object request, object? response)
    {
        Console.WriteLine($"Finished {request.GetType().Name}");
        return Task.CompletedTask;
    }
}

// Register it
builder.Services.AddScoped<IInterceptor, LoggingInterceptor>();

How it Works

Meditation includes an Incremental Source Generator that scans your project for classes implementing IHandler. It then generates a Dispatcher class that contains a large switch statement (pattern matching).

When you call dispatcher.Dispatch(request), it matches the request object against known types and calls the appropriate handler directly. No reflection, no dictionaries, no overhead.

// Example of generated code:
public async Task<TResponse> Dispatch<TResponse>(IRequest<TResponse> request, CancellationToken ct)
{
    var result = await (request switch
    {
        PingQuery typed => (Task<TResponse>)(object)_pingHandler.HandleAsync(typed, ct),
        _ => throw new InvalidOperationException(...)
    });
    return result;
}

Installation

Add the following projects to your solution:

  • Meditation.Abstractions: Contains the interfaces.
  • Meditation: The Source Generator project.

Source Code