Skip to content

dzmprt/MitMediator

Repository files navigation

MitMediator

Fast mediator for handling requests, commands, notifications, and streams with ValueTask and ordered pipelines

Build and Test NuGet .NET 10 Coverage NuGet Downloads License

Features

  • Supports IRequest<TResponse> and IRequest (void-style)
  • Custom handlers via IRequestHandler<,> and IRequestHandler<>
  • Supports both ValueTask (for modern, efficient handlers) and Task (for compatibility with MediatR-style handlers)
  • Enables middleware-style pipelines using IPipelineBehavior<TRequest, TResponse>. A pipeline behavior can be defined for all or specific request types.
  • Supports INotificationHandler with serial and parallel publishing
  • Ordered execution of pipeline behaviors
  • Simple registration through AddMitMediator() or assembly scanning
  • Supports IStreamRequestHandle and IStreamPipelineBehavior

Getting Started

Installation

dotnet add package MitMediator -v 10.0.0

Example Usage

This example shows a basic setup of MitMediator that demonstrates:

  • Request handling via PingRequestHandler
  • Notification publishing via NotificationHandler
  • Two pipeline behaviors: HeightBehavior and LowBehavior
using Microsoft.Extensions.DependencyInjection;
using MitMediator;

var services = new ServiceCollection();
services
    .AddMitMediator(typeof(PingRequestHandler).Assembly)
    .AddTransient(typeof(IPipelineBehavior<,>), typeof(HeightBehavior<,>))
    .AddTransient(typeof(IPipelineBehavior<,>), typeof(LowBehavior<,>));

var provider = services.BuildServiceProvider();
var mediator = provider.GetRequiredService<IMediator>();

// HeightBehavior: Handling PingRequest
// LowBehavior: Handling PingRequest
// PingRequestHandler: Pong
// NotificationHandler: Notification!
// LowBehavior: Handled PingRequest
// HeightBehavior: Handled PingRequest
string result = await mediator.SendAsync<PingRequest, string>(new PingRequest(), CancellationToken.None);

Console.WriteLine(result); //Pong result

public class PingRequest : IRequest<string> { }

public class PingRequestHandler : IRequestHandler<PingRequest, string>
{
    private readonly IMediator _mediator;

    public PingRequestHandler(IMediator mediator)
    {
        _mediator = mediator;
    }
    
    public ValueTask<string> HandleAsync(PingRequest request, CancellationToken cancellationToken)
    {
        Console.WriteLine("PingRequestHandler: Pong");
        _mediator.PublishAsync(new Notification(), cancellationToken);
        return ValueTask.FromResult("Pong result");
    }
}

public class LowBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IRequest<TResponse>
{
    public async ValueTask<TResponse> HandleAsync(TRequest request, IRequestHandlerNext<TRequest, TResponse> next, CancellationToken cancellationToken)
    {
        Console.WriteLine($"LowBehavior: Handling {typeof(TRequest).Name}");
        var result = await next.InvokeAsync(request, cancellationToken);
        Console.WriteLine($"LowBehavior: Handled {typeof(TRequest).Name}");
        return result;
    }
}

public class HeightBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IRequest<TResponse>
{
    public async ValueTask<TResponse> HandleAsync(TRequest request, IRequestHandlerNext<TRequest, TResponse> next, CancellationToken cancellationToken)
    {
        Console.WriteLine($"HeightBehavior: Handling {typeof(TRequest).Name}");
        var result = await next.InvokeAsync(request, cancellationToken);
        Console.WriteLine($"HeightBehavior: Handled {typeof(TRequest).Name}");
        return result;
    }
}

public class Notification : INotification{}

public class NotificationHandler : INotificationHandler<Notification>
{
    public ValueTask HandleAsync(Notification notification, CancellationToken cancellationToken)
    {
        Console.WriteLine($"NotificationHandler: Notification!");
        return ValueTask.CompletedTask;
    }
}

To use Task instead of ValueTask for handlers, reference the MitMediator.Tasks namespace

Migrating from MediatR

You can reuse your existing handlers with minimal modifications — just update the namespaces and registration calls

  1. Add the MitMediator package dotnet add package MitMediator -v 10.0.0
  2. In your request files, replace the namespace MediatR with MitMediator
  3. In your request handler files, replace the namespace MediatR with MitMediator (andMitMediator.Tasks for Task result)
  4. Update your dependency injection setup: replace .AddMediatR(...) with .AddMitMediator()
  5. If you're implementing INotificationHandler, use ValueTask instead of Task
  6. If you're implementing IPipelineBehavior, use ValueTask instead of Task and IRequestHandlerNext<TRequest, TResponse> instead of RequestHandlerDelegate<TResponse>. Use next.InvokeAsync(request, cancellationToken) for next pipe
  7. For handlers with void result, use Task<Unit> instead of Task (return Unit.Value)
  8. (Optional) Change all mediator.Send(request, ct) to mediator.SendAsync<TRequset, TResponse>(request, ct) (or mediator.Send<TRequset, TResponse>(request, ct) for Task result)
  9. Build and run your project — you’re all set!

Use SendAsync<TRequset, TResponse>(request, ct) for best performance or Send(request, ct) for backward compatibility with MediatR-style semantics

MitMediator is designed to feel familiar for those coming from MediatR. Core concepts like IRequest, IRequestHandle, and pipeline behaviors are preserved — but with a cleaner interface and support for ValueTask out of the box.

Comparison: MitMediator vs. MediatR

Performance

Mediator Method Mean (ns) Allocated (B)
MediatR Send (return result) 91.64 272
MitMediator SendAsync (return result) 40.60 0
MediatR Send (return result, use behaviors) 191.82 800
MitMediator SendAsync (return result, use behaviors) 40.49 0
MediatR Send (Return void) 77.73 128
MitMediator SendAsync (Return void) 36.73 0
MediatR Publish 138.79 592
MitMediator PublishAsync 51.74 32
MediatR CreateStream (return stream, use behavior) 809.3 1168
MitMediator CreateStream (return stream, use behavior) 187.0 112

Features

Feature MitMediator MediatR
Return types ValueTask (default, allocation-friendly) Task (standard async support)
Send methods Strongly typed requests (SendAsync<TRequest, TResponse>) Loosely typed requests (Send(request))
DI Registration AddMitMediator() with optional assembly scanning AddMediatR() with assemblies explicitly specified
Extensibility Designed for lightweight extension and customization More opinionated; extensibility requires deeper integration
Notification publishing Serial and parallel Only serial out of the box
Performance Focus Async-first, zero-allocation for ValueTask Flexible but not optimized for ValueTask
License & Availability MIT Reciprocal Public License 1.5 (RPL1.5) and commercial license

Extensions

License

MIT

About

Fast mediator for handling requests, commands, notifications, and streams with ValueTask and ordered pipelines

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages