Mediary 1.0.0

dotnet add package Mediary --version 1.0.0
                    
NuGet\Install-Package Mediary -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Mediary" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Mediary" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Mediary" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Mediary --version 1.0.0
                    
#r "nuget: Mediary, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Mediary@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Mediary&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Mediary&version=1.0.0
                    
Install as a Cake Tool

Mediary — Lightweight Request Dispatcher for .NET

Mediary is a minimal, open-source library for .NET that implements the Request/Handler (Mediator) pattern with pipeline support — inspired by MediatR, but built from scratch with no third-party dependencies.

Clean request handling, extensible pipeline behaviors, and a DI-friendly architecture.

Build codecov License: MIT .NET NuGet

🚨 Why Mediary?

Mediary is a lightweight request dispatcher for .NET designed to offer a clean, extensible, and dependency-free alternative to more complex mediators.

It focuses on performance, clarity, and developer control, while maintaining compatibility with the .NET dependency injection ecosystem.

✅ Key benefits

  • Lightweight and fast — no unnecessary overhead or runtime reflection
  • 🧩 Extensible pipeline behaviors — clean middleware-style request handling
  • 🧼 Minimalist design — no third-party dependencies, no magic
  • 🧪 Test-friendly — everything is composable and DI-compatible
  • 📦 NuGet-ready — simple to install and integrate

🧭 Table of Contents


📦 Installation

You can install Mediary via NuGet:

dotnet add package Mediary

Or via the NuGet UI in Visual Studio by searching for Mediary.

📦 Available at: https://www.nuget.org/packages/Mediary


🚀 Features


🔧 Quick Start

1. Define a Request

public class GetAllPlansQuery : IQuery<List<PlanDto>> { }

2. Create a Handler

public class GetAllPlansHandler : IRequestHandler<List<PlanDto>, GetAllPlansQuery>
{
    public Task<List<PlanDto>> HandleAsync(GetAllPlansQuery request)
    {
        // Your logic here
    }
}

3. Inject the Dispatcher

public class PlanService
{
    private readonly IRequestDispatcher _dispatcher;

    public PlanService(IRequestDispatcher dispatcher) =>
        _dispatcher = dispatcher;

    public Task<List<PlanDto>> GetPlansAsync() =>
        _dispatcher.DispatchAsync<List<PlanDto>, GetAllPlansQuery>(new GetAllPlansQuery());
}

🛠 Dependency Injection

Mediary supports flexible registration depending on your needs:

services.AddMediary()
    .AddRequestHandlersFromAssembly(typeof(Program).Assembly)
    .AddPipelineBehaviorsFromAssembly(typeof(Program).Assembly);

This will:

🔧 Option 2 — Semi-manual (Builder API)

If you prefer full control but want to use the builder pattern:

services.AddMediary()
    .AddRequestHandler<List<PlanDto>, GetAllPlansQuery, GetAllPlansHandler>()
    .AddPipelineBehaviors<List<PlanDto>, GetAllPlansQuery, LoggingBehavior<List<PlanDto>, GetAllPlansQuery>>();

You can also override the dispatcher:

services.AddMediary<CustomDispatcher>();

🧩 Option 3 — Fully manual

If you want zero coupling to Mediary's builder or extension methods, you can register everything manually:

services.AddScoped<IRequestDispatcher, RequestDispatcher>();
services.AddScoped<IRequestHandler<List<PlanDto>, GetAllPlansQuery>, GetAllPlansHandler>();
services.AddScoped<IRequestPipelineBehavior<List<PlanDto>, GetAllPlansQuery>, LoggingBehavior<List<PlanDto>, GetAllPlansQuery>>();

This gives you maximum flexibility and full control over dependency injection.


🔍 Pipeline Behaviors

Pipeline behaviors wrap request handling and allow you to implement cross-cutting concerns like logging, validation, caching, etc.

Built-in Behaviors

Behavior Description
LoggingBehavior<TResponse, TRequest> Logs the start and end of a request using ILogger.
PerformanceBehavior<TResponse, TRequest> Measures and logs execution time of each request.

Both behaviors support the [RequestInfo] attribute for enriched log output.

Custom Behaviors

Implement IRequestPipelineBehavior<TResponse, TRequest> to create your own:

public class ValidationBehavior<TResponse, TRequest> : IRequestPipelineBehavior<TResponse, TRequest>
    where TRequest : IRequest<TResponse>
{
    public async Task<TResponse> HandleAsync(TRequest request, Func<Task<TResponse>> next)
    {
        // Logic before the handler
        var response = await next();
        // Logic after the handler
        return response;
    }
}

Global Registration

You can register behaviors globally for all requests:

Using the MediaryBuilder
services.AddMediary()
    .AddOpenPipelineBehaviors(typeof(LoggingBehavior<,>))
    .AddOpenPipelineBehaviors(typeof(PerformanceBehavior<,>));
Manual registration
services.AddScoped(typeof(IRequestPipelineBehavior<,>), typeof(LoggingBehavior<,>));
services.AddScoped(typeof(IRequestPipelineBehavior<,>), typeof(PerformanceBehavior<,>));

🪪 Aliases

For semantic clarity, Mediary exposes aliases for intent-based request types:

Alias Inherits Purpose
IQuery<TResponse> IRequest<TResponse> Read-only request
ICommand<TResponse> IRequest<TResponse> Write command

Aliases are optional and meant to improve readability:

// Represents: GET /users/{id}
public class GetUserByIdQuery : IQuery<UserDto> { }

// Represents: POST /users
public class CreateUserCommand : ICommand<UserDto> { }

✅ Result Types

When you don't need to return data from a command, you can use semantic result types to indicate intent:

Type Purpose
Unit Used when no response is needed (replaces void)
Success Indicates a generic successful result
Created Indicates a resource was created
Updated Indicates a resource was updated
Deleted Indicates a resource was deleted

These types are lightweight readonly structs defined in Mediary.Core.Results.

Example: Command with no output
public class ClearCacheCommand : IRequest<Unit> { }

public class ClearCacheHandler : IRequestHandler<Unit, ClearCacheCommand>
{
    public Task<Unit> HandleAsync(ClearCacheCommand request) =>
        Task.FromResult(Unit.Value);
}
Example: Command with semantic return
public class CreateUserCommand : IRequest<Created> { }

public class CreateUserHandler : IRequestHandler<Created, CreateUserCommand>
{
    public Task<Created> HandleAsync(CreateUserCommand request) =>
        Task.FromResult(Created.Value);
}
Using the Result helper

The static Result class provides convenient access to all result singletons:

Result.Unit      // equivalent to Unit.Value
Result.Success   // equivalent to Success.Value
Result.Created   // equivalent to Created.Value
Result.Updated   // equivalent to Updated.Value
Result.Deleted   // equivalent to Deleted.Value

🔖 Request Metadata

You can optionally annotate your request types with descriptive metadata using the [RequestInfo] attribute:

[RequestInfo("Creates a new user", "Command", "Users")]
public class CreateUserCommand : ICommand<Guid> { }

This helps document the purpose of the request and can be consumed by logging, debugging, or inspection tools.

Both built-in behaviors (LoggingBehavior and PerformanceBehavior) will use this description if available.

Extension Methods

The metadata is accessible at runtime via extension methods on IRequest<TResponse>:

request.GetDescription()  // Returns the description string, or null
request.GetTags()         // Returns the tags array
request.GetInfo()         // Returns the full RequestInfoAttribute, or null

📄 License

MIT License — Free for personal and commercial use.


🙌 Contributing

Open to feedback, ideas, PRs and improvements — feel free to fork and collaborate.


👨‍💻 Author

Built with ❤️ by Facundo JuarezGitHub

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 149 3/2/2026
0.3.0 142 7/27/2025
0.2.0 130 7/18/2025
0.1.0 177 7/15/2025